搭建流程:
1、首先创建表,使用shiro制作权限验证至少要又用户表、角色表、角色权限表、菜单地址表。
表结构图式例:
user:
2、pom文件依赖。
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.7.RELEASE
com.syzw.shiro.test
shiro_demo
0.0.1-SNAPSHOT
shiro_demo
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-logging
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-log4j
1.3.8.RELEASE
com.baomidou
mybatis-plus-boot-starter
3.0.5
org.projectlombok
lombok
1.18.8
commons-logging
commons-logging
1.1.3
org.apache.shiro
shiro-core
1.2.3
org.apache.shiro
shiro-spring
1.3.2
com.auth0
java-jwt
3.4.0
org.xmlunit
xmlunit-core
org.springframework.boot
spring-boot-maven-plugin
3、自定义Realm。
/**
* @program: shiro_demo
* @description:
* 自定义的Realm数据源,用于给安全管理器做鉴权认证使用,realm在主体对象Subject调用login进行认证的时候,
* Subject将认证委托给安全管理器SecurityManager,SecurityManager最终还是会到达这个地方,执行这里的认证逻辑的。
* @author: hyly
* @create: 2019-08- 19:46
*/
public class MyRealm extends AuthorizingRealm {
//操作用户
@Autowired
private UserMapper userMapper;
//操作角色表
@Autowired
private RoleMapper roleMapper;
//操作权限表
@Autowired
private PermissionMapper permissionMapper;
//具体的菜单权限
@Autowired
private MenuMapper menuMapper;
public MyRealm(){
//因为数据库中的密码做了散列,所以使用shiro的散列Matcher,如果数据库中没有使用md5加密的话,加上这个地方会报错
// this.setCredentialsMatcher(new HashedCredentialsMatcher(Md5Hash.ALGORITHM_NAME));
}
//
/**
* 执行授权业务逻辑,主要用于做权限认证,也就是那些可以访问,那些不能访问
* 这个地方就是用来给登录的角色进行赋权的,也就是说从这个地方获取登录的用户有那些权限,并且把这些权限告诉shiro,
* 然后shiro就可以和拦截器中的拦截规则去验证,你是否有访问权限了。
*
* 这个地方需要访问数据库,获取当前用的权限,添加到shiro中。
*
* 这个方法之后登录成功之后才会访问
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//创建简单的授权信息
SimpleAuthorizationInfo simpleAuthenticationInfo=new SimpleAuthorizationInfo();
String userName= (String) principals.getPrimaryPrincipal();
System.out.println(userName);
//授权
if(userName!=null){
QueryWrapper userWrapper=new QueryWrapper<>();
userWrapper.eq("name",userName);
User user=userMapper.selectOne(userWrapper);
if(user!=null){
System.out.println(userName);
//查询当前登录用户的所有所有角色
QueryWrapper wrapper=new QueryWrapper<>();
wrapper.eq("id",user.getRole());
List roleList=roleMapper.selectList(wrapper);
//将遍历list,将list中role的的角色名取出,并且放到set集合中。
Set roles=roleList.stream().map(Role::getName).collect(Collectors.toSet());
//将所有的角色信息添加进授权信息中
simpleAuthenticationInfo.setRoles(roles);
//查询出来角色对应的权限id
QueryWrapper permissionWrapper=new QueryWrapper<>();
permissionWrapper.eq("id",user.getRole());
List permissionList=permissionMapper.selectList(permissionWrapper);
//所有的menu的id,也就是具体菜单的id
Set menuIds=permissionList.stream().map(Permission::getMenu).collect(Collectors.toSet());
if(menuIds.size()>0){
//根据权限id查询权限路径
QueryWrapper
4、配置shiro的配置文件。
/**
* @program: shiro_demo
* @description: 用于配置Shiro的安全管理器,Realm源以及一些shiro拦截器
* @author: hyly
* @create: 2019-08- 19:44
*/
@Configuration
public class ShiroConfig {
/**
* 设置shiro权限拦截器
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
//创建shiro拦截器工厂,就像spring-mvc的拦截器链一样,ShiroFilterFactoryBean这个对象就像过滤器链
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
//和jwt的token拦截验证绑定起来
Map filter=new HashMap<>();
filter.put("jwt", new JwtFilter());
shiroFilterFactoryBean.setFilters(filter);
/**
* shiro内置过滤器:
* anon:无需认证(登录),可以直接访问。
* authc:必须进行认证才能访问。
* user:如果使用rememberMe的功能,可以直接访问呢。
* perms:该资源必须得到资源的权限才可以进行访问。
* role:该资源必须得到角色的权限才可以进行访问。
*
* * URL 匹配风格
* * 1). ?:匹配一个字符,如 /admin? 将匹配 /admin1,但不匹配 /admin 或 /admin/;
* * 2). *:匹配零个或多个字符串,如 /admin* 将匹配 /admin 或/admin123,但不匹配 /admin/1;
* * 2). **:匹配路径中的零个或多个路径,如 /admin/** 将匹配 /admin/a 或 /admin/a/b
* *
* * 配置身份验证成功,失败的跳转路径
*
*/
//获取拦截规则
Map filterMap=NoVerify.getNoerifyList();
//当用户没有登录时跳转的界面
shiroFilterFactoryBean.setLoginUrl("/user/login2");
//设置用户没有权限的默认跳转界面
shiroFilterFactoryBean.setUnauthorizedUrl("/user/noAuthen");
//登录成功的路径
shiroFilterFactoryBean.setSuccessUrl("/index");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
/**
* 创建出来安全管理器,用于进行鉴权使用
* @param realm
* @return
*/
@Bean("securityManager")
public DefaultWebSecurityManager getDefaultSecurityManager(@Qualifier("myRealm")MyRealm realm){
//创建出来一个安全管理器
DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
//让创建出来的安全管理器使用自己自定义的realm源进行认证
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
/**
*创建自定义的realm源,供安全管理器使用进行鉴权,可以提供多个
* @return
*/
@Bean("myRealm")
public MyRealm createMyRealm(){
MyRealm myRealm=new MyRealm();
//认证匹配器,用于给前端明文密码加密,和数据库取出的密文密码做检验
myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myRealm;
}
/**
* 配置Shiro生命周期处理器,让shiro自动加载很多默认的配置
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
/**
* 自动创建代理类,若不添加,Shiro的注解可能不会生效。
*/
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new
DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 开启Shiro的注解
*
* 这个地方的@Qualifier("securityManager") DefaultWebSecurityManager securityManager可能会有问题,这个是我自己加的
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new
AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 凭证匹配器
*
* (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 )
* 因为数据库的密码加密了,而前端的输入的密码是明文密码,这样子去和数据库取出的密码做比较显然是不配的,
* 因此就要设置加密规则,shiro在密码比较的时候,就会将前端的明文密码加密之后再进行对比。
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new
HashedCredentialsMatcher();
//设置加密规则
hashedCredentialsMatcher.setHashAlgorithmName(Md5Hash.ALGORITHM_NAME);
//设置系统凭证的编码转换格式,也就是密码转成什么格式去比对,默认为true,表示16进制,
hashedCredentialsMatcher.setStoredCredentialsHexEncoded(false);
//设置散列次数
hashedCredentialsMatcher.setHashIterations(2);
return hashedCredentialsMatcher;
}
/**
* Shiro方言,支持Thymeleaf中使用shiro标签,如果在前端要使用这个shiro的标签,则需要导入Thymeleaf依赖
*/
// @Bean
// public ShiroDialect shiroDialect() {
// return new ShiroDialect();
// }
}
5、controller模拟跳转页面。
/**
* @program: shiro_demo
* @description: 用户模块
* @author: hyly
* @create: 2019-08- 21:14
*/
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private UserMapper userMapper;
@GetMapping("/login")
@ResponseBody
public String login(String userName, String password){
//将前端的用户名和密码存放到shiro中,这里不需要加密,因为realm源中设置了CredentialsMatcher
//CredentialsMatcher认证匹配器中设置了加密规则
UsernamePasswordToken token=new UsernamePasswordToken();
token.setPassword(password.toCharArray());
token.setUsername(userName);
/**
这里的账号密码到时候会自动使用认证器中的盐给这个明文加盐,然后和数据库查询出来的密文做匹配。
*/
//获取用户主体对象
Subject subject= SecurityUtils.getSubject();
try{
//进行认证
subject.login(token);
}catch (Exception exception){
exception.printStackTrace();
return "登录失败,用户名或者密码不对";
}
//判断是否登录
if(subject.isAuthenticated()){
return "登录成功";
}else{
return "登录失败";
}
}
/**
* 用户登出
*/
@GetMapping("/logout")
public String logout(){
Subject subject= SecurityUtils.getSubject();
subject.logout();
return"用户已经登出";
}
/**
* 添加用户
* @param user
* @return
*/
@PostMapping("/add")
public Integer add(@RequestBody User user){
Random random=new Random();
String salt=random.nextInt()+"";
Md5Hash md5Hash2=new Md5Hash(user.getPassword());
System.out.println(md5Hash2.toBase64());
Md5Hash md5Hash=new Md5Hash(user.getPassword(),salt);
user.setSalt(salt);
user.setPassword(md5Hash.toBase64());
Integer reuslt = userMapper.insert(user);
return reuslt;
}
/**
* 获取用户列表
* @return
*/
@GetMapping("/getList")
public List getList(){
QueryWrapper wrapper=new QueryWrapper<>();
List list=userMapper.selectList(wrapper);
return list;
}
/**
* 模拟错误错误页面
* @return
*/
@GetMapping("/error")
public String error(){
return "500错误页面";
}
/**
* 模拟用户没有权限页面
* @return
*/
@GetMapping("/noAuthen")
public String noAuthen(){
return "权限不足";
}
/**
* 模拟跳转用户登录页面
* @return
*/
@GetMapping("/login2")
public String login(){
return "用户登录页面";
}
@RequiresPermissions("/exam/paperManager")
@GetMapping("/paper")
public String paper(){
return "试卷管理";
}
}
6、启动项目,就可以测试了。