问题描述
使用Shiro框架实现权限管理进行并盐值加密是工作开发最常用的代码,我这里总结成对应的模板,做到即用即拿,CV开发效率变快!
解决方案:
创建springboot项目,创建时选择导入springboot web和thmyeleaf(不用不导)
并且导入spring和shiro的整合包,这里依赖版本要一样,不同版本配置稍微不同
<!-- Subject用户 SecurityManager管理所有用户 Realm连接数据 --> <!--shiro-spring--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.8.0</version> </dependency>
数据库模拟,这里用到了权限字段perms,别的具体看自己的业务开发
CREATE TABLE `user`( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(20), pwd VARCHAR(32), email VARCHAR(200), perms VARCHAR(20) );
下面就是基于perms这个权限字段来进行书写
ShiroConfig配置类、UserRealm授权认证类、登录注册类(这个也是重点,如果你用盐值加密注册要盐值加密对应起来,不然检查不了登录不上)
首先ShiroConfig配置类(主要下面的加密配置和引入)
/** * @author mrs * @create 2022-04-30 17:53 */ @Configuration public class ShiroConfig{ /** * MD5或者MD5盐值加密都要注入对应的加密的配置 * 只要加密都要配! * 重点!! * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher(){ HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); //setHashAlgorithmName设置使用的加密算法的名称 hashedCredentialsMatcher.setHashAlgorithmName("MD5"); //设置加密的次数 hashedCredentialsMatcher.setHashIterations(1024);//这个要与注册那边对应 return hashedCredentialsMatcher; } //第一步:创建realm对象,导入对应的加密方式配置 @Bean public UserRealm userRealm(HashedCredentialsMatcher hashedCredentialsMatcher){ UserRealm userRealm = new UserRealm(); //替换当前realm对象的credentialsMatcher属性进行密码比对,hashedCredentialsMatcher中对密码加密。 userRealm.setCredentialsMatcher(hashedCredentialsMatcher); return userRealm; } //第二步:创建安全对象 @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //关联userRealm securityManager.setRealm(userRealm); return securityManager; } //第三步:创建过滤工厂ShiroFilerFactoryBeanShiro过滤的一些对象 @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //设置安全管理器 bean.setSecurityManager(defaultWebSecurityManager); /* 添加内置shiro过滤器 anon:无需认证就可以访问 authc:必须认证了才能访问 user:必须拥有记住我功能才能用 perms:拥有对某个资源的权限才能访问 role:拥有某个角色权限才能访问 */ Map<String,String> filterMap = new LinkedHashMap();//这里一定要使用LinkedHashMap! //这里一定要放行静态资源! //先写小的放行,再写大的放行 filterMap.put("/static/**","anon"); filterMap.put("/logout","logout");//设置退出登录请求 filterMap.put("/bad/vip/**","perms[vip]");//中括号里面就是表示对应权限字段perms的值,进行区分权限! filterMap.put("/bad/**","authc"); bean.setFilterChainDefinitionMap(filterMap); bean.setLoginUrl("/index.html");//如果没有登录就调到登陆页面 bean.setUnauthorizedUrl("/bad/noauth");//权限不够的跳转页面,俗称充钱页面 return bean; } }
UserRealm授权认证类(设置对应MD5/MD5盐值加密对应的检查机制)
/** * @author mrs * @create 2022-04-30 17:54 */ public class UserRealm extends AuthorizingRealm { @Autowired UserService userService; //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection){ SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //拿到当前登录的这个对象 Subject subject = SecurityUtils.getSubject(); User currentUser = (User)subject.getPrincipal(); info.addStringPermission(currentUser.getPerms()); return info; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException{ //上面的参数就是authenticationToken就是Controller获取的token UsernamePasswordToken userToken = (UsernamePasswordToken)authenticationToken; //连接真实的数据库 User user = userService.queryUserByName(userToken.getUsername()); if(user==null){//没有这个人 return null;//抛出异常UnknownAccountException } /** * 密码认证shiro自动配置 * 密码加密:MD5/MD5盐值加密(建议MD5盐值加密,已经在shiroConfig配置类配置了对应的加密方式) * 返回的对象new SimpleAuthenticationInfo( * 用户对象, * 用户密码(是数据库中查到的注册MD5/MD5盐值加密的加密过的密码), * 盐值加密的对象(设置检查对应MD5盐值加密密码的正确性,后面注册是要加密方式一样,不然会验证不了,如果使用MD5加密不写这个参数因为在shiro中已经设置了MD5,这个参数可以省略), * 对应的Realm的值(作用不大,一般是对应用户的账号名)); */ //创建对应的盐值加密对象,在里面设置对应的盐值,这里加入了对应用户名的大写字符串 ByteSource bytes = ByteSource.Util.bytes(user.getUsername().toUpperCase()); return new SimpleAuthenticationInfo(user,user.getPwd(),bytes,user.getUsername()); } }
对应写的请求参数的Controller类,其中包括登录、注册、用户权限不够跳转的页面
/** * @author mrs * @create 2022-04-30 22:07 */ @Controller public class IndexController { @Autowired UserService service; @RequestMapping("/tologin") public String toLogin(){ return "index"; } @RequestMapping("/login") public String login(String username, String password, Model model, HttpSession session){ //获取当前用户 Subject subject = SecurityUtils.getSubject(); //封装用户的登录数据 UsernamePasswordToken token = new UsernamePasswordToken(username,password); try{ subject.login(token);//执行登录的方法,如果没有异常就说明ok了 session.setAttribute("loginUser",username); return "beddings/dashboard"; }catch(UnknownAccountException uae){//用户名不对提示 model.addAttribute("msg","用户名错误"); return "index"; }catch(IncorrectCredentialsException ice){//密码不对提示 model.addAttribute("msg","密码错误"); return "index"; } } /** * 俗称,权限不够的请求,跳转充钱页面的请求 * @return */ @RequestMapping("/bad/noauth") public String unauthorized(){ return "jurisdiction"; } //注册 @RequestMapping("/regist") public String regist(User user,Model model){ if(service.existsUsername(user.getUsername())){ /** * 这里是注册的重点,在我的shiro检查对应盐值加密的密码时 * 1、在ShiroConfig类中设置了对应的密码加密设置,参数就是"MD5"和1024 * 2、在UserRealm类中设置了对应用户名的大写字符串当盐值 * 所以这四个参数为:MD5、前端接收的密码、用户名的大写字符串(对应的盐值)、1024(对应的加密次数) */ String md5 = new SimpleHash("MD5", user.getPwd(),user.getUsername().toUpperCase(), 1024).toHex(); user.setPwd(md5); service.saveUser(user); return "index"; }else { model.addAttribute("msg","用户名重复"); model.addAttribute("email",user.getEmail()); return "register"; } } }
博客中的代码原型是狂神的Shiro框架视频中的结构,这里进行改进加入了一些实际的应用,比如MD5盐值加密等等。如果理解难可以收藏再看B站的狂神Shiro视频,再回来查看会好一点。