springboot|springboot+shiro+redis实现博客权限管理(前后端分离)

博客地址:https://www.lqnb.xyz/
源码地址:https://github.com/memo012/ac-blog
springboot+shiro+redis实现博客权限管理(前后端分离) 一. 背景 现如今,项目的安全权限问题越来越受重视,比如springsecurity,shiro…都可以用于权限管理,被称为权限框架。此博客介绍spring boot整合shiro。
二. 环境
  • springboot1.x或springboot2.x
  • shiro 1.4.0版本
  • redis任意
三. 搭建步骤 1. 导包
org.apache.shiro shiro-spring 1.4.0 org.crazycake shiro-redis 3.1.0 org.projectlombok lombok 1.18.6

整个pom.xml中文件
4.0.0【springboot|springboot+shiro+redis实现博客权限管理(前后端分离)】org.springframework.boot spring-boot-starter-parent 2.1.6.RELEASE com.qiang shiro-redis 0.0.1-SNAPSHOT shiro-redis Demo project for Spring Boot1.8 1.3.3 org.springframework.boot spring-boot-starter-web org.mybatis.spring.boot mybatis-spring-boot-starter ${mybatis.version} mysql mysql-connector-java org.springframework.boot spring-boot-starter-test test com.alibaba druid 1.1.6 org.apache.shiro shiro-spring 1.4.0 org.projectlombok lombok 1.18.6 org.springframework.boot spring-boot-starter-thymeleaf org.crazycake shiro-redis 3.1.0 org.springframework.boot spring-boot-maven-plugin

二. 自定义realm(AuthRealm)
package com.qiang.config; import com.qiang.dao.UserDao; import com.qiang.entity.PermissionEntity; import com.qiang.entity.RoleEntity; import com.qiang.entity.UserEntity; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * @Author: qiang * @ProjectName: adminsystem * @Package: com.qiang.config * @Description: * @Date: 2019/7/28 0028 20:53 **/ public class AuthRealm extends AuthorizingRealm {@Autowired private UserDao userDao; /** * 授权 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("授权 doGetAuthorizationInfo"); // 获取用户信息 UserEntity userEntity = (UserEntity) principals.getPrimaryPrincipal(); UserEntity byUsername = userDao.findByUsername(userEntity.getUsername()); List roles = new ArrayList<>(); List permissions = new ArrayList<>(); Set roleEntiries = byUsername.getRoleEntiries(); System.out.println(roleEntiries.size()); if(roleEntiries.size() > 0 && roleEntiries != null){ for (RoleEntity r: roleEntiries) { roles.add(r.getRname()); if(r.getPermissionEntities().size() > 0 && r.getPermissionEntities() != null){ for (PermissionEntity p: r.getPermissionEntities()) { permissions.add(p.getName()); System.out.println(p.getName()); } }} } SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); simpleAuthorizationInfo.addRoles(roles); simpleAuthorizationInfo.addStringPermissions(permissions); return simpleAuthorizationInfo; }/** * 认证 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("认证 doGetAuthenticationInfo"); String username = (String)token.getPrincipal(); System.out.println(username); UserEntity byUsername = userDao.findSimpleByUsername(username); String password = byUsername.getPassword(); if(password == null || password.equals("")){ return null; } System.out.println(password); //当前realm对象的name String realmName = getName(); //盐值 ByteSource credentialsSalt = ByteSource.Util.bytes(byUsername.getUsername()); SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(byUsername, byUsername.getPassword(),credentialsSalt, realmName); return simpleAuthenticationInfo; } }

三. sessionManager自定义会话管理(CustomSessionManager )
package com.qiang.config; import org.apache.shiro.web.servlet.ShiroHttpServletRequest; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.apache.shiro.web.util.WebUtils; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.Serializable; /** * @Author: qiang * @ProjectName: adminsystem * @Package: com.qiang.config * @Description: shiro中sessionManager自定义会话管理 * @Date: 2019/7/28 0028 21:48 **/ public class CustomSessionManager extends DefaultWebSessionManager {private static final String AUTHORIZATION = "token"; public CustomSessionManager() { super(); }@Override protected Serializable getSessionId(ServletRequest request, ServletResponse response) { String sessionId = WebUtils.toHttp(request).getHeader(AUTHORIZATION); if (sessionId != null) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId); //automatically mark it valid here.If it is invalid, the //onUnknownSession method below will be invoked and we'll remove the attribute at that time. request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); return sessionId; } else { return super.getSessionId(request, response); } }}

四. shiro配置文件(ShiroConfig.java)
package com.qiang.config; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.crazycake.shiro.RedisCacheManager; import org.crazycake.shiro.RedisManager; import org.crazycake.shiro.RedisSessionDAO; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.Filter; import java.util.LinkedHashMap; import java.util.Map; /** * @Author: qiang * @ProjectName: adminsystem * @Package: com.qiang.config * @Description: * @Date: 2019/7/28 0028 21:19 **/ @Configuration public class ShiroConfig {@Bean("shiroFilter") public ShiroFilterFactoryBean shiroFilter() { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(securityManager()); bean.setLoginUrl("/pub/need_login"); // 登录成功 如何前后端分离(不调用) bean.setSuccessUrl("/"); // 没有权限,未授权就会调用此方法 先登录-》再验证是否有权限 bean.setUnauthorizedUrl("/pub/not_permit"); // 设置自定义filter Map filterMap = new LinkedHashMap<>(); filterMap.put("roleOrFilter", new CustomRoleOrAuthorizationFilter()); bean.setFilters(filterMap); // 拦截器路径 Map map = new LinkedHashMap<>(); // 退出过滤器 map.put("/logout", "logout"); // 匿名访问 游客即可 map.put("/pub/**", "anon"); map.put("/index", "anon"); // 认证才可访问 map.put("/authc/**", "authc"); // 管理员才可访问 map.put("/admin/**", "roles[admin]"); // 管理员才可访问 只需具备任意角色中一个即可访问(自定义AuthorizationFilter) // map.put("/admin/**", "roleOrFilter[admin,root]"); // 管理员才可访问 默认必须具备全部角色才可访问 // map.put("/admin/**", "roles[admin,root]"); // 拥有某权限才可访问 map.put("/video/update", "perms[video_update]"); // 坑二 过滤器是顺序执行,从上而下,一般将/** 放到最底下// authc: 认证才可访问 // anon: 匿名即可 map.put("/**", "anon"); bean.setFilterChainDefinitionMap(map); return bean; }@Bean("securityManager") public SecurityManager securityManager() { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); // 如果前后端不分离,则注释掉即可 manager.setSessionManager(sessionManager()); // 使用自定义cacheManager manager.setCacheManager(redisCacheManager()); // 推荐放到最后 manager.setRealm(authRealm()); return manager; }/** * 自定义realm * * @return */ @Bean("authRealm") public AuthRealm authRealm() { AuthRealm authRealm = new AuthRealm(); authRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return authRealm; }/** * 密码加解密规则 * * @return */ @Bean("hashedCredentialsMatcher") public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); // 散列算法:使用MD5加密 hashedCredentialsMatcher.setHashAlgorithmName("MD5"); // 散列次数 hashedCredentialsMatcher.setHashIterations(1024); hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true); return hashedCredentialsMatcher; }/** * 自定义sessionManager * * @return */ @Bean("sessionManager") public SessionManager sessionManager() { CustomSessionManager customSessionManager = new CustomSessionManager(); // 超时时间 默认30分钟,会话超时,方法里面的是单位是毫秒 //customSessionManager.setGlobalSessionTimeout(60 * 1000); // 配置session持久化 customSessionManager.setSessionDAO(redisSessionDAO()); return customSessionManager; }/** * 设置redisManager * @return */ public RedisManager getRedisManager(){ RedisManager redisManager = new RedisManager(); redisManager.setHost("192.168.233.133"); redisManager.setPort(6379); return redisManager; }/** * 配置具体cache实现类 * @return */ @Bean("redisCacheManager") public RedisCacheManager redisCacheManager(){ RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(getRedisManager()); // 设置过期时间,单位是秒 redisCacheManager.setExpire(60 * 30); return redisCacheManager; }/** * 自定义session持久化 * @return */ public RedisSessionDAO redisSessionDAO(){ RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(getRedisManager()); // 设置sessionId生成器 redisSessionDAO.setSessionIdGenerator(new CustomSessionIdGenerator()); return redisSessionDAO; }/** * 开启 Shiro aop 注解支持 * 用代理方式; 所以需要开启代码支持 * @return advisor */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager()); return advisor; } }

五. 密码加密类(ShiroMD5.java)
package com.qiang.config; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.util.ByteSource; /** * @Author: qiang * @ProjectName: adminsystem * @Package: com.qiang.shiro * @Description:shiroMD5加密类 * @Date: 2019/6/21 0021 13:17 **/ public class ShiroMD5 {public static Object MD5(String username,String password){ String hashAlgorithName = "MD5"; int hashIterations = 1024; //加密次数 ByteSource credentialsSalt = ByteSource.Util.bytes(username); Object obj = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations); return obj; }public static void main(String[] args) { System.out.println(MD5("dong", "123456")); } }

到此,整合完成
具体代码:https://github.com/memo012/ac-blog
公众号 希望大家多多关注,里面不定期发放干货
领取全套资料:回复关键字【666】
springboot|springboot+shiro+redis实现博客权限管理(前后端分离)
文章图片

    推荐阅读