当前位置: 首页 > news >正文

Shiro会话管理和加密

会话管理

        Shiro提供了完整的企业级会话管理功能,不依赖于底层容器(如Tomcat),不管是J2SE还是J2EE环境都可以使用,提供了会话管理,会话事件监听,会话存储/持久化,容器无关的集群,失效/过期支持,对Web的透明支持,SSO单点登录的支持等特性。

会话相关API

  • Subject.getSession(): 获取当前Subject的会话。如果当前没有会话,根据参数的不同会有不同的行为:
    • Subject.getSession(true): 如果当前没有会话,则创建一个新的会话。
    • Subject.getSession(false): 如果当前没有会话,则返回null。\
  • session.setAttribute(Object key, Object value): 设置会话属性。
  • session.getAttribute(Object key): 根据键获取会话属性。
  • session.removeAttribute(Object key): 根据键删除会话属性。

SessionDAO

  • AbstractSessionDAO类:这是SessionDAO的一个基本实现类,提供了生成会话ID等基础功能。
  • CachingSessionDAO类:这是一个提供了缓存功能的实现类,它位于应用层与持久化层之间,用于减少频繁请求持久化层的开销。它重写了AbstractSessionDAO中的部分方法,并实现了SessionDAO中的其他方法。不过,CachingSessionDAO中的doUpdate和doDelete方法是预留给子类去实现的。
  • MemorySessionDAO类:这是一个将Session保存在内存中的实现类,其存储结构是ConcurrentHashMap。不过,在实际项目中,这个实现类用得并不多。
  • EnterpriseCacheSessionDAO类:这是一个提供了缓存功能的Session维护实现类。它设置了默认的缓存管理器(AbstractCacheManager)和默认的缓存实例(MapCache),并实现了缓存效果。不过,它并没有实现持久化操作,只是简单地提供了缓存实现。开发者可以继承这个类,并重写doCreate、doReadSession、doUpdate和doDelete等方法来实现持久化操作。

缓存

具体实现

1. 添加依赖

        <!-- Shiro配置Redis缓存管理 --><dependency><groupId>org.crazycake</groupId><artifactId>shiro-redis</artifactId><version>3.3.1</version></dependency>

2. application.properties或 application.yml配置文件中添加Redis配置

spring:#Redis配置data:redis:# Redis 服务器的主机名或 IP 地址host: localhost# Redis 服务器的端口号,默认是 6379port: 6379# Redis 服务器密码password: luoqiangwu# Redis 的 lettuce 客户端连接池配置lettuce:pool:# 连接池最大活动连接数,默认为 8max-active: 8# 连接池最大阻塞等待时间,-1 表示无限等待max-wait: -1# 连接池中的最大空闲连接数,默认为 8max-idle: 8# 连接池中最小空闲连接数,默认为 0min-idle: 0timeout: 5000

3. ShiroConfig

@Configuration
public class ShiroConfig {// 注入Redis参数,从application.yml获得@Value("${spring.data.redis.host}")private String host;@Value("${spring.data.redis.port}")private int port;@Value("${spring.data.redis.password}")private String password;@Value("${spring.data.redis.timeout}")private int timeout;@Resourceprivate RoleService roleService;/*** 开启Shiro注解(如@RequiresRoles,@RequiresPermissions),* 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证* 配置以下两个 bean(DefaultAdvisorAutoProxyCreator 和 AuthorizationAttributeSourceAdvisor)*/@Beanpublic DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();advisorAutoProxyCreator.setProxyTargetClass(true);return advisorAutoProxyCreator;}/*** 开启AOP注解支持*/@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}/*** 验证匹配规则** @return*/@Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();// 使用 md5 算法进行加密hashedCredentialsMatcher.setHashAlgorithmName("md5");// 设置散列次数:意为加密几次hashedCredentialsMatcher.setHashIterations(1024);return hashedCredentialsMatcher;}/*** 自定义Realm(基于数据库)*/@Beanpublic MyShiroRealm myShiroRealm() {MyShiroRealm shiroRealm = new MyShiroRealm();// 设置启用缓存,并设置缓存名称shiroRealm.setCachingEnabled(true);shiroRealm.setAuthorizationCachingEnabled(true);shiroRealm.setAuthorizationCacheName("authorization");// 设置凭证(密码)匹配器shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());return shiroRealm;}/*** Thymeleaf页面上使用shiro标签*/@Bean(name = "shiroDialect")public ShiroDialect shiroDialect() {return new ShiroDialect();}/*** Redis管理器*/@Beanpublic RedisManager redisManager() {RedisManager redisManager = new RedisManager();redisManager.setHost(host + ":" + port); // #shiro-redis v3.3.1redisManager.setPassword(password);redisManager.setTimeout(timeout);return redisManager;}/*** 缓存管理器*/@Beanpublic CacheManager shiroCacheManager() {RedisCacheManager cacheManager = new RedisCacheManager();cacheManager.setRedisManager(redisManager());// 缓存名称cacheManager.setPrincipalIdFieldName("usrName");// 缓存有效时间cacheManager.setExpire(1800);return cacheManager;}/*** 会话持久化操作*/@Beanpublic RedisSessionDAO redisSessionDAO() {RedisSessionDAO sessionDAO = new RedisSessionDAO();sessionDAO.setRedisManager(redisManager());return sessionDAO;}/*** 安全管理器SecurityManager*/@Beanpublic SecurityManager securityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 注入自定义的RealmsecurityManager.setRealm(myShiroRealm());// 注入缓存管理器securityManager.setCacheManager(shiroCacheManager());// 注入会话管理器securityManager.setSessionManager(sessionManager());// Shiro2.0.1新版本需要SecurityUtils设置SecurityManager,否则报错(org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code)SecurityUtils.setSecurityManager(securityManager);return securityManager;}/*** 会话管理*/@Beanpublic DefaultWebSessionManager sessionManager() {DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();sessionManager.setSessionDAO(redisSessionDAO());return sessionManager;}/*** Shiro过滤器:权限验证*/@Beanpublic ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();// 注入SecurityManagershiroFilterFactory.setSecurityManager(securityManager);// 权限验证:使用 Filter 控制资源(URL)的访问shiroFilterFactory.setLoginUrl("/login"); // 登录页面URLshiroFilterFactory.setSuccessUrl("/main"); // 登录成功URLshiroFilterFactory.setUnauthorizedUrl("/403"); // 没有权限跳转403页面// 权限配置集合,必须使用LinkedHashMap(有序集合)Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();// 静态资源授权filterChainDefinitionMap.put("/css/**", "anon");filterChainDefinitionMap.put("/fonts/**", "anon");filterChainDefinitionMap.put("/images/**", "anon");filterChainDefinitionMap.put("/js/**", "anon");filterChainDefinitionMap.put("/localcss/**", "anon");filterChainDefinitionMap.put("/localjs/**", "anon");// API接口请求授权filterChainDefinitionMap.put("/api/**", "anon");// 登录退出页面设置filterChainDefinitionMap.put("/login", "anon"); // 点击登录按钮时放行filterChainDefinitionMap.put("/logout", "logout"); // 点击退出按钮时放行// 配置需要特定权限才能访问的资源(URL)// 静态授权:包括全部需要特定权限才能访问的资源(URL)/*filterChainDefinitionMap.put("/user/list", "perms[用户列表]");filterChainDefinitionMap.put("/user/add", "perms[用户添加]");filterChainDefinitionMap.put("/user/edit", "perms[用户编辑]");filterChainDefinitionMap.put("/user/del", "perms[用户删除]");*/// 动态授权List<Right> rights = roleService.findAllRights();for (Right right : rights) {if (right.getRightUrl() != null && !right.getRightUrl().trim().equals("")) {filterChainDefinitionMap.put(right.getRightUrl(), "perms[" + right.getRightCode() + "]");}}// 配置认证访问:其他资源(URL)必须认证通过才能访问filterChainDefinitionMap.put("/**", "authc"); // 必须放在过滤器链的最后面shiroFilterFactory.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactory;}
}

加密

哈希与盐

        如果你需要保存密码,你要考虑如何保护这些密码数据,像下面那样直接将密码写入数据库中是极不安全的,因为任何可以打开数据库的人,都将可以直接看到这些密码。

        解决的办法是将密码加密后再存储进数据库,比较使用的加密方法是使用哈希函数(散列算法),常见的散列算法如MD5、SHA等。哈希函数的具体定义,大家可以在网上或者相关书籍中查询到,简单地说,它的特性如下:

  1. 原始密码经哈希函数计算后得到一个哈希值
  2. 改变原始密码,哈希函数计算出的哈希值也会相应改变
  3. 同样的密码,哈希值也是相同的

哈希函数是单向、不可逆的。也就是说从哈希值,你无法推算出原始的密码是多少。

加密与验证

        Shiro提供了PasswordService及CredentialsMatcher用于提供加密密码及验证密码服务。

public interface PasswordService {String encryptPassword(Object var1) throws IllegalArgumentException;boolean passwordsMatch(Object var1, String var2);
}

 具体实现

MyShiroRealm

public class MyShiroRealm extends AuthorizingRealm {@Lazy // Shiro框架执行比@Cacheable注解AOP代理早,导致对象代理不成功@Resourceprivate UserService userService;@Lazy // Shiro框架执行比@Cacheable注解AOP代理早,导致对象代理不成功@Resourceprivate RoleService roleService;/*** 自定义认证流程(机制)*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("调用 MyShiroRealm.doGetAuthenticationInfo 获取身份信息!");// 获得身份信息UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String userName = token.getUsername();// 从数据库中根据用户名查询用户对象(联表查询,包含角色信息)User user = userService.findUserByUsrName(userName);if (user == null) {throw new UnknownAccountException("用户不存在"); // 用户不存在}if (user.getUsrFlag() == null || user.getUsrFlag() == 0) {throw new LockedAccountException("账号已锁定");}// 通过用户获取角色信息Role role = user.getRole(); // int &207 -> list// 根据角色获取权限列表Set<Right> rights = roleService.findRightByRoleId(role.getRoleId());// 给角色设置权限集合role.setRights(rights);System.out.println(user);SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getUsrPassword(), ByteSource.Util.bytes("czkt"), this.getName());// 返回身份信息return info;}/*** 自定义授权(认证通过才会授权)*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("调用 MyShiroRealm.doGetAuthorizationInfo 获取权限信息!");// 获得权限信息User user = (User) principalCollection.getPrimaryPrincipal();SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();// 静态授权:授予主体(用户)相应的角色和权限/*info.addRole(user.getRole().getRoleName());info.addStringPermission("用户列表"); // 所有用户拥有"用户列表"权限if ("管理员".equals(user.getRole().getRoleName())) { // 管理员拥有"增删改"权限info.addStringPermission("用户添加");info.addStringPermission("用户编辑");info.addStringPermission("用户删除");}*/// 动态授权:从数据库中获取角色和权限Role role = user.getRole();if (role != null) {info.addRole(role.getRoleName()); // 动态设置角色Set<Right> rights = role.getRights();if (rights != null && !rights.isEmpty()) {for (Right right : rights) {info.addStringPermission(right.getRightCode()); // 动态设置权限}}}// 返回授权信息return info;}}

http://www.mrgr.cn/news/60558.html

相关文章:

  • 鸿蒙网络编程系列32-基于拦截器的性能监控示例
  • [MySQL#3] 数据约束 | 数值类 | varchar | timestamp | enum vs set
  • Python_PyCharm无法打开终端命令行最终解决方案(实测)
  • IntelliJ IDEA 查看类class的结构Structure轮廓outline窗口, 快捷键是Alt+7
  • C#编程进阶:深入理解属性与索引器
  • 力扣382:链表随机结点
  • 医院信息化与智能化系统(11)
  • 阳振坤:云时代数据库的思考 | OceanBase发布会实录
  • 【高级IO】IO多路转接之epoll
  • 移植FreeRTOS实时操作系统(基于STM32F429)
  • Java运行时数据区
  • C++基础三
  • 华为大咖说丨如何通过反馈机制来不断优化大模型应用?
  • 销售拓客秘籍:线上渠道探寻与选择
  • 2024年【北京市安全员-A证】找解析及北京市安全员-A证考试试卷
  • 一周倒计时!2024中山智能家居峰会议程揭晓
  • 深入理解数据链路层:以太网帧格式、MAC地址、交换机、MTU及ARP协议详解与ARP欺骗探究
  • 300元蓝牙耳机性价比高的有哪些?学生平价蓝牙耳机推荐
  • 10位三维设计如何共享工作站算力和软件
  • Instagram如何加特效?轻松打造个性化动态效果的实用指南
  • ChatGPT:从发布到全球大热,仅用一年多的传奇之旅
  • pgloader的简单数据迁移教程
  • 推荐一款好用的redis管理工具TinyRDM
  • 三维模型加载慢的问题优化
  • 置换环模板题E - Permute K times 2
  • 数字后端零基础入门系列 | Innovus零基础LAB学习Day7