粗暴一点,上图
展开是这样的
由于我把用户角色和密码放在了一张用户表上,这里Role类以及Mapper并没有使用
另外为了只是简单的测试,没有加上权限操作字段,使用表如下
<dependencies>
//controller层需要
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
//数据库连接
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
//mybatis整合
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
//测试
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
//shiro整合
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
//lombok使用
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
</dependencies>
log4j.properties
# Configure logging for testing: optionally with log file
log4j.rootLogger=info, stdout
# log4j.rootLogger=WARN, stdout, logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
application.properties
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/shirodemo?useUnicode=true&useSSL=false&characterEncoding=utf8&&serverTimezone=GMT&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=olonn
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis.mapper-locations=classpath:mapper/*.xml
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<!-- 开启二级缓存 -->
<!-- <cache type="org.mybatis.caches.ehcache.LoggingEhcache"/>-->
<select id="selectPasswordByName" resultType="java.lang.String">
SELECT password FROM USER WHERE username = #{name}
</select>
<select id="selectRoleByName" resultType="java.lang.String">
SELECT role FROM USER WHERE username = #{name}
</select>
</mapper>
User
@Data
public class User implements Serializable {
private int id;
private String username;
private String password;
private String role;
}
ShiroConfig
这是shiro配置的核心,我们要把自定义realm、加密方式等注入securityManager对象,然后把该对象放入shiroFilterFactoryBean,并配置请求的url需要用户登陆或者有无权限,权限信息为
@Configuration
public class ShiroConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(ShiroConfig.class);
@Bean
public UserRealm getRealm(){
return new UserRealm();
}
@Bean(name = "securityManager")
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(getRealm());
return securityManager;
}
//这里可以进行加密方式配置
@Bean
public HashedCredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
hashedCredentialsMatcher.setHashIterations(3);
return hashedCredentialsMatcher;
}
@Bean
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
shiroFilterFactoryBean.setLoginUrl("/notLogin");
//无权限时的跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/noRole");
//设置拦截器
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
//游客,开发权限
filterChainDefinitionMap.put("/guest/**","anon");
//用户
filterChainDefinitionMap.put("/user/**","roles[user]");
//管理员
filterChainDefinitionMap.put("/admin/**","roles[admin]");
//登陆接口
filterChainDefinitionMap.put("/login/**","anon");
//拦截所有其他接口,必须放在所有权限验证的最后,不然会把所有url全部拦截
filterChainDefinitionMap.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
LOGGER.info("shiro拦截工厂类启动成功");
return shiroFilterFactoryBean;
}
}
@Mapper
public interface UserMapper {
/**
* fetch data by rule id
*
* @param username
* @return Result<String>
*/
String selectPasswordByName(@Param("name") String username);
/**
* fetch data by rule id
*
* @param username
* @return Result<String>
*/
String selectRoleByName(@Param("name") String username);
}
@Component
public class UserRealm extends AuthorizingRealm {
private static final Logger LOGGER = LoggerFactory.getLogger(UserRealm.class);
@Autowired
private UserMapper userMapper;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
LOGGER.info("-----权限验证-----");
String username = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRole(userMapper.selectRoleByName(username));
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
LOGGER.info("-----身份验证-----");
String username = (String) token.getPrincipal();
String password = userMapper.selectPasswordByName(username);
if (null==password){
throw new AccountException("用户名不正确");
}
return new SimpleAuthenticationInfo(username,password,getName());
}
private ByteSource generateSalt(String salt){
return ByteSource.Util.bytes(salt);
}
}
为了简单示例,没有加加密操作,另外登陆失败的异常没有处理
LoginController
@RestController
public class LoginController {
private static HashMap<String,String> myp = new HashMap<>(16);
@RequestMapping("/notLogin")
@ResponseBody
public HashMap<String, String> notlogin(){
myp.put("hello","world");
return myp;
}
@RequestMapping("/login/{username}/{password}")
@ResponseBody
public HashMap<String, String> login(@PathVariable("username")String username,@PathVariable("password")String password){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
subject.login(token);
myp.put("for","login");
return myp;
}
@RequestMapping("/noRole")
@ResponseBody
public HashMap<String, String> noRole(){
myp.put("for","login");
return myp;
}
}
UserController
@RestController
public class UserController {
private static HashMap<String ,String > mymap = new HashMap<>(16);
@RequestMapping("/admin/{admin}")
@ResponseBody
public HashMap admin(@PathVariable("admin")String admin){
mymap.put("admin",admin);
return mymap;
}
@RequestMapping("/user/{user}")
@ResponseBody
public HashMap user(@PathVariable("user")String user){
mymap.put("user",user);
return mymap;
}
@RequestMapping("/guest/{guest}")
@ResponseBody
public HashMap guest(@PathVariable("guest")String guest){
mymap.put("guest",guest);
return mymap;
}
}
上面代码算是全部写完了,接下来验证
运行springboot启动类
访问http://localhost:8080/xxxxxx
自动跳转到/notLogin
访问http://localhost:8080/guest/xxxx
访问http://localhost:8080/user/zzzz同样跳转到notLogin
访问http://localhost:8080/login/ccc/123
找不到用户,后台抛出异常未处理
访问http://localhost:8080/login/bbb/123
登陆成功
然后访问http://localhost:8080/user/我登陆了
可以成功访问
访问http://localhost:8080/admin/mmmm
自动跳转到/noRole