springboot_shiro_redis整合(一)-shiro-权限控制
springboot_shiro_redis整合(一)-shiro-权限控制
目录
文章目录
- 一、前言
- 二、shiro整合redis
- 1、分析
- 2、pom.xml 导入依赖
- 2、相关具体类
- 2.1、封装ShiroUtils 工具类
- 2.2、UserRealm 自定义realm
- 2.3、ShiroConfig 配置类
- 2.4、CustomSessionManager 身份标志获取
- ***后记*** :
内容
一、前言
目前常用的安全框架为spring security、shiro等,关于二者之间的比较,有兴趣的小伙伴自行查阅相关文档这里不再讲述。这里,我们选择相对轻巧且功能能满足我们项目需要的shiro框架。
shiro框架默认基于cookie-session来管理安全信息,而当前前后端分离、微服务、分布式广泛应用的情况下,显然不太适用。通过参考查询相关文档,其中之一的解决方案是用redis来替换。下面我们就来讲述shiro配置和shiro整合redis。
二、shiro整合redis
1、分析
之前基于cookie-session,用户(subject)通过认证后,信息存入session,身份标志sessionID存入cookie,返回客户端。每次客户端访问服务端,需要通过cookie携带该身份标志,shiro验证通过,然后执行后续操作。
- 改造
- 封装shiro工具类
- realm
- 整合redis配置
- 身份标志获取
2、pom.xml 导入依赖
1.4.0 0.0.9
com.ihrm ihrm-common 1.0.0-SNAPSHOT org.apache.shiro shiro-core ${shiro.version} org.apache.shiro shiro-spring ${shiro.version}
com.github.axet kaptcha ${kaptcha.version} org.crazycake shiro-redis 3.1.0
- 解析:
- shiro-core:shiro核心
- shiro-spring: 用于spring整合shiro
- shiro-redis: 第三方shiro整合redis
- kapcha :第三方验证码生成
2、相关具体类
2.1、封装ShiroUtils 工具类
package com.ihrm.modules.sys.shiro;import com.ihrm.common.exception.RRException;
import com.ihrm.modules.sys.entity.UserEntity;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;/*** Shiro工具类*/
public class ShiroUtils {/** 加密算法 */public final static String hashAlgorithmName = "md5";/** 循环次数 */public final static int hashIterations = 16;public static String md5(String password, String salt) {return new SimpleHash(hashAlgorithmName, password, salt, hashIterations).toString();}public static Session getSession() {return SecurityUtils.getSubject().getSession();}public static Subject getSubject() {return SecurityUtils.getSubject();}public static UserEntity getUserEntity() {return (UserEntity) SecurityUtils.getSubject().getPrincipal();}public static String getId() {return getUserEntity().getId();}public static void setSessionAttribute(Object key, Object value) {getSession().setAttribute(key, value);}public static Object getSessionAttribute(Object key) {return getSession().getAttribute(key);}public static boolean isLogin() {return SecurityUtils.getSubject().getPrincipal() != null;}public static void logout() {SecurityUtils.getSubject().logout();}public static String getKaptcha(String key) {Object kaptcha = getSessionAttribute(key);if(kaptcha == null){throw new RRException("验证码已失效");}getSession().removeAttribute(key);return kaptcha.toString();}}
2.2、UserRealm 自定义realm
package com.ihrm.modules.sys.shiro;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ihrm.common.utils.Constant;
import com.ihrm.modules.sys.dao.PermissionDao;
import com.ihrm.modules.sys.dao.UserDao;
import com.ihrm.modules.sys.entity.PermissionEntity;
import com.ihrm.modules.sys.entity.UserEntity;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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 org.springframework.stereotype.Component;import java.util.*;/*** 认证*/
@Component
public class UserRealm extends AuthorizingRealm {@Autowiredprivate UserDao sysUserDao;@Autowiredprivate PermissionDao permissionDao;/*** 授权(验证权限时调用)*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {UserEntity user = (UserEntity)principals.getPrimaryPrincipal();String userId = user.getId();List permsList;//系统管理员,拥有最高权限if(StringUtils.equals(userId,Constant.SUPER_ADMIN)){List menuList = permissionDao.selectList(null);permsList = new ArrayList<>(menuList.size());for(PermissionEntity menu : menuList){permsList.add(menu.getPerms());}}else{permsList = permissionDao.queryBtnsByUserId(userId);}//用户权限列表Set permsSet = new HashSet<>();for(String perms : permsList){if(StringUtils.isBlank(perms)){continue;}permsSet.addAll(Arrays.asList(perms.trim().split(",")));}SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setStringPermissions(permsSet);return info;}/*** 认证(登录时调用)*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {UsernamePasswordToken token = (UsernamePasswordToken)authcToken;//查询用户信息UserEntity user = sysUserDao.selectOne(new QueryWrapper().eq("mobile", token.getUsername()));//账号不存在if(user == null) {throw new UnknownAccountException("账号或密码不正确");}//账号锁定if(user.getEnableState() == 0){throw new LockedAccountException("账号已引用,请联系管理员");}SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), ByteSource.Util.bytes(user.getMobile()), getName());return info;}@Overridepublic void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {HashedCredentialsMatcher shaCredentialsMatcher = new HashedCredentialsMatcher();shaCredentialsMatcher.setHashAlgorithmName(ShiroUtils.hashAlgorithmName);shaCredentialsMatcher.setHashIterations(ShiroUtils.hashIterations);super.setCredentialsMatcher(shaCredentialsMatcher);}/*** 清除当前用户的权限认证缓存** @param principals 权限信息 身份集合*/@Overridepublic void clearCache(PrincipalCollection principals) {super.clearCache(principals);}
}
2.3、ShiroConfig 配置类
package com.ihrm.common.config;import com.ihrm.modules.sys.shiro.CustomSessionManager;
import com.ihrm.modules.sys.shiro.UserRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.LinkedHashMap;
import java.util.Map;/*** Shiro的配置文件*/
@Configuration
public class ShiroConfig {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private int port;@Value("${spring.redis.password}")private String password;@Value("${spring.redis.timeout}")private int timeout;// @Bean(name = "redisSessionDAO")
// public RedisSessionDAO sessionDAO() {
// return new RedisSessionDAO();
// }@Beanpublic RedisManager redisManager() {RedisManager redisManager = new RedisManager();
// redisManager.setHost(host);
// redisManager.setTimeout(timeout);
// if (StringUtils.isNotBlank(password))
// redisManager.setPassword(password);return redisManager;}@Beanpublic RedisCacheManager redisCacheManager() {RedisCacheManager cacheManager = new RedisCacheManager();cacheManager.setRedisManager(redisManager());return cacheManager;}@Beanpublic RedisSessionDAO redisSessionDAO() {RedisSessionDAO redisSessionDAO = new RedisSessionDAO();redisSessionDAO.setRedisManager(redisManager());return redisSessionDAO;}/*** 单机环境,session交给shiro管理* 会话管理器*/@Bean("sessionManager")@ConditionalOnProperty(prefix = "ihrm", name = "cluster", havingValue = "false")public DefaultWebSessionManager sessionManager(@Value("${ihrm.globalSessionTimeout:3600}") long globalSessionTimeout) {CustomSessionManager sessionManager = new CustomSessionManager();// redis sessionsessionManager.setSessionDAO(redisSessionDAO());sessionManager.setSessionValidationSchedulerEnabled(true);// 禁用重写url// sessionManager.setSessionIdUrlRewritingEnabled(false);// 禁用session 放在cookie// sessionManager.setSessionIdCookieEnabled(false);sessionManager.setSessionValidationInterval(globalSessionTimeout * 1000);// 会话过期时间,单位:毫秒(在无操作时开始计时)--->一分钟,用于测试sessionManager.setGlobalSessionTimeout(globalSessionTimeout * 1000);return sessionManager;}/*** 集群环境,session交给spring-session管理*/@Bean@ConditionalOnProperty(prefix = "ihrm", name = "cluster", havingValue = "true")public ServletContainerSessionManager servletContainerSessionManager() {return new ServletContainerSessionManager();}private SimpleCookie rememberMeCookie() {// 设置 cookie 名称,对应 login.html 页面的 SimpleCookie cookie = new SimpleCookie("rememberMe");// 设置 cookie 的过期时间,单位为秒 一个月cookie.setMaxAge(3600 * 30);return cookie;}private CookieRememberMeManager rememberMeManager() {CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();cookieRememberMeManager.setCookie(rememberMeCookie());// rememberMe cookie 加密的密钥cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));return cookieRememberMeManager;}@Bean("securityManager")public SecurityManager securityManager(UserRealm userRealm, SessionManager sessionManager) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// securityManager.setCacheManager(new EhCacheManager());securityManager.setCacheManager(redisCacheManager());securityManager.setRealm(userRealm);securityManager.setSessionManager(sessionManager);securityManager.setRememberMeManager(rememberMeManager());return securityManager;}@Bean("shiroFilter")public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();shiroFilter.setSecurityManager(securityManager);shiroFilter.setLoginUrl("/login");shiroFilter.setUnauthorizedUrl("/");// shiroFilter.setSuccessUrl("/index.html");//自定义过滤器
// Map filterMap1 = new LinkedHashMap<>();
//
// filterMap1.put("user",new MyUserFilter());
// shiroFilter.setFilters(filterMap1);Map filterMap = new LinkedHashMap<>();filterMap.put("/swagger/**", "anon");
// filterMap.put("/v2/api-docs", "anon");filterMap.put("/swagger-ui.html", "anon");filterMap.put("/webjars/**", "anon");filterMap.put("/swagger-resources/**", "anon");filterMap.put("/doc.html", "anon");filterMap.put("/statics/**", "anon");filterMap.put("/favicon.ico", "anon");filterMap.put("/sys/captcha", "anon");filterMap.put("/sys/login", "anon");
// filterMap.put("/sys/getInfo", "anon");filterMap.put("/**", "authc");shiroFilter.setFilterChainDefinitionMap(filterMap);return shiroFilter;}@Bean("lifecycleBeanPostProcessor")public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}@Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();credentialsMatcher.setHashAlgorithmName("MD5"); //加密方式credentialsMatcher.setHashIterations(16);//散列次数return credentialsMatcher;}
}
2.4、CustomSessionManager 身份标志获取
package com.ihrm.modules.sys.shiro;import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;public class CustomSessionManager extends DefaultWebSessionManager {/*** 获取请求头中key为“Authorization”的value == sessionId*/private static final Logger log = LoggerFactory.getLogger(CustomSessionManager.class);private static final String TOKEN = "Authorization";private static final String REFERENCED_SESSION_ID_SOURCE = "header";/*** @Description shiro框架 自定义session获取方式
* 可自定义session获取规则。这里采用ajax请求头 Authorization 携带sessionId的方式*/@Overrideprotected Serializable getSessionId(ServletRequest request, ServletResponse response) {String id = WebUtils.toHttp(request).getHeader(TOKEN);if (StringUtils.isBlank(id)) {id = WebUtils.toHttp(request).getParameter(TOKEN);}// 如果请求头中有 Authorization 则其值为sessionIdif (StringUtils.isNotBlank(id)) {log.info("请求头中获取");request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);return id;} else {log.info("默认方式获取sessionId");return super.getSessionId(request, response);}}
}
后记 :
本项目为参考某马视频开发,相关视频及配套资料可自行度娘或者联系本人。上面为自己编写的开发文档,持续更新。欢迎交流,本人QQ:806797785
后端JAVA源代码地址:https://gitee.com/gaogzhen/ihrm-parent // 后端项目
前端项目源代码地址:https://gitee.com/gaogzhen/ihrm-vue // 前端后台管理系统
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
