Shiro

1.会话管理

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

 

1.1会话相关API

Subject.getSession(): 获取当前用户的会话,如果当前没有创建会话对象,则会创建一个新的会话。这等价于Subject.getSession(true)。

Subject.getSession(boolean create): 根据参数决定是否创建一个新的会话。如果create为true且当前没有会话,则创建一个新的会话;如果为false且当前没有会话,则返回null。

session.setAttribute(key, value): 设置会话属性。

session.getAttribute(key): 获取会话属性。

session.removeAttribute(key): 删除会话属性。

会话使用时,建议在Controller层使用原生的HttpSession对象,在Service层使用Shiro提供的Session对象。

 

1.2SeeionDAO

 

 

Shiro提供SessionDOA用于会话持久化提供CRUD操作。

 

AbstractSessionDAO:提供了SessionDAO 的基础实现,如生成会话ID等。

CachingSessionDAO :提供了对开发者透明的会话缓存的功能,需要设置相应的

CachingSessionDOA。

MemorySessionDA0:直接在内存中进行会话维护。

EnterpriseCacheSessionDA0:提供了缓存功能的会话维护,默认情况下使用MapCache 实现,内部使用ConcurrentHashMap保存缓存的会话。

在实际开发中,如果要用到SessionDA0组件,可以自定义类实现自EnterpriseCacheSessionDA0类,为其注入sessionldGenerator属性,如果用到缓存的话还可以注入一个缓存的实现。然后将这个 SesionDA0组件注入给SessionManager(会话管理器),最后将SessionManager 配置给 SecurityManager。下一小节我们将实现数据缓存,将使用到该组件。

 

2.缓存

1.添加依赖

 

       

            org.crazycake

            shiro-redis

            3.3.1

           

               

                    redis.clients

                    jedis

               

           

       

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

 

  # Redis配置

  data:

    redis:

      host: 127.0.0.1

      port: 6379

      password: 密码

      database: 0

      timeout: 5000

      lettuce:

        pool:

# 连接池最大连接数(使用负值表示没有限制): 8

          max-active: 8

          # 连接池最大阻塞等待时间(使用负值表示没有限制)默认为-1

          max-wait: -1

          # 连接池中的最大空闲连接 默认为8

          max-idle: 8

          # 连接池中的最小空闲连接 默认为0

          min-idle: 0

3.改造ShiroConfig

 

import com.bdqn.demo.pojo.Right;

import com.bdqn.demo.service.RightService;

import jakarta.annotation.Resource;

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;

import org.apache.shiro.mgt.SecurityManager;

import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;

import org.apache.shiro.web.mgt.DefaultWebSecurityManager;

import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;

import org.crazycake.shiro.RedisCacheManager;

import org.crazycake.shiro.RedisManager;

import org.crazycake.shiro.RedisSessionDAO;

import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

 

import java.util.LinkedHashMap;

import java.util.List;

import java.util.Map;

/*

 shiro安全框架配置类

 */

@Configuration

public class ShiroConfig {

    @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;

 

    /**

     * 开启shiro注解支持(如@RequiresRoles,@RequiresPermissions)

     * 需要借助springAop扫描使用shiro注解的类,并在必要时进行安全逻辑验证

     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能

     *

     * @return

     */

    @Bean

    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {

        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();

        advisorAutoProxyCreator.setProxyTargetClass(true);

        return advisorAutoProxyCreator;

    }

 

    /**

     * 配置shiro注解支持

     * 开启shiro aop注解支持.

     *

     * @return

     */

    @Bean

    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {

        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();

        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);

        return authorizationAttributeSourceAdvisor;

    }

 

    public RedisManager redisManager() {

        RedisManager redisManager = new RedisManager();

        redisManager.setHost(host + ":" + port);

        redisManager.setPassword(password);

        redisManager.setTimeout(timeout);

        return redisManager;

    }

 

    public RedisCacheManager cacheManager() {

        RedisCacheManager redisCacheManager = new RedisCacheManager();

        redisCacheManager.setRedisManager(redisManager());

        // 缓存名称

        redisCacheManager.setPrincipalIdFieldName("usrName");

        // 缓存有效时间

        redisCacheManager.setExpire(1800);

        return redisCacheManager;

    }

 

    // 会话操作

    public RedisSessionDAO redisSessionDAO() {

        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();

        redisSessionDAO.setRedisManager(redisManager());

        return redisSessionDAO;

    }

 

    // 会话管理器

    public DefaultWebSessionManager sessionManager() {

        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();

        sessionManager.setSessionDAO(redisSessionDAO());

        return sessionManager;

    }

 

    /**

     * 加密算法

     */

    @Bean

    public HashedCredentialsMatcher hashedCredentialsMatcher() {

        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

        hashedCredentialsMatcher.setHashAlgorithmName("md5");

        hashedCredentialsMatcher.setHashIterations(2); // 散列的次数,比如散列两次

        return hashedCredentialsMatcher;

    }

 

    @Bean

    public MyShiroRealm myShiroRealm() { // 自定义的realm

        MyShiroRealm shiroRealm = new MyShiroRealm();

        // 设置启用缓存,并设置缓存名称

        shiroRealm.setCachingEnabled(true);

        shiroRealm.setAuthorizationCachingEnabled(true);

        shiroRealm.setAuthorizationCacheName("authorizationCache");

// 设置凭证(密码)匹配器

        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());

        return shiroRealm;

    }

 

    @Bean

    public SecurityManager securityManager() { // 安全管理器

        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        // 注入Realm

        securityManager.setRealm(myShiroRealm());

        // 注入缓存管理器

        securityManager.setCacheManager(cacheManager());

        // 注入会话管理器

        securityManager.setSessionManager(sessionManager());

        return securityManager;

    }

 

    @Resource

    private RightService rightService;

 

    @Bean

    public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager) { // shiro过滤器

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 注入SecurityManager

        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 权限验证使用Filter控制资源(URL)的访问

        shiroFilterFactoryBean.setLoginUrl("/login");

        shiroFilterFactoryBean.setSuccessUrl("/main");

        shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // 没有权限跳转403页面

 

        Map filterChainDefinitionMap = new LinkedHashMap<>();// 必须使用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");

        filterChainDefinitionMap.put("/login", "anon");

        filterChainDefinitionMap.put("/logout", "logout"); // 注销过滤器,自动注销

 

        // 配置需要特定权限才能访问的资源(url)

        // 动态授权:包括全部需要特定权限才能访问的资源(url)

        List rights = rightService.list();

        for (Right right : rights) {

            if (right.getRightUrl() != null && !"".equals(right.getRightUrl().trim())) {

                filterChainDefinitionMap.put(right.getRightUrl().trim(), "perms[" + right.getRightCode() + "]");

                String s = filterChainDefinitionMap.get(right.getRightUrl().trim());

                System.out.println("动态权限:==" + right.getRightUrl().trim() + "===" + s);

            }

        }

 

        // 配置认证访问:其他所有资源都需要认证才能访问

        filterChainDefinitionMap.put("/**", "authc"); // 必须放在过滤器链的最后面

 

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        SecurityUtils.setSecurityManager(securityManager);

        return shiroFilterFactoryBean;

    }

 

 

    @Bean(name = "shiroDialect")

    public ShiroDialect shiroDialect() {

        return new ShiroDialect(); // 用于thymeleaf和shiro标签配合使用

    }

}

3.加密

1. 加密概述

 

加密是保护数据安全的重要手段。Shiro提供了多种加密方式和工具,帮助开发者实现数据的加密和解密。

 

2. HashedCredentialsMatcher

 

HashedCredentialsMatcher是Shiro提供的一个用于密码加密和验证的工具。它支持多种哈希算法(如MD5、SHA-256等)和哈希迭代次数,以提高密码的安全性。在配置Realm时,可以定义一个HashedCredentialsMatcher属性,用于密码的加密和验证。

 

3. 盐值加密

 

为了提高密码的安全性,Shiro支持使用盐值进行加密。盐值是一个随机生成的字符串,与密码一起进行哈希运算,使得即使两个用户设置了相同的密码,生成的哈希值也是不同的。在Shiro中,盐值可以在返回AuthenticationInfo时带上,SecurityManager会根据提交的明文密码、加密算法、加密次数和盐值进行加密,然后与AuthenticationInfo中的密码进行比对。

 

4. 常见加密算法

 

Shiro支持多种常见的加密算法,如MD5、SHA-1、SHA-256等。MD5是一种广泛使用的哈希算法,但存在安全性问题,容易被破解。SHA-1和SHA-256等算法则相对更安全,适用于对安全性要求较高的场景。

 

4、Shiro会话管理与加密实践

1. 配置Shiro会话管理

 

在Shiro配置类中,可以配置会话管理器、会话监听器、会话存储等参数。例如,在Spring Boot环境中,可以通过Java配置类来配置Shiro的会话管理:

 

@Configuration  

public class ShiroConfig {  

    // ...  

    @Bean  

    public DefaultWebSessionManager sessionManager() {  

        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();  

        // 配置会话超时时间等参数  

        sessionManager.setGlobalSessionTimeout(1800000); // 30分钟  

        // 配置会话DAO等  

        // sessionManager.setSessionDAO(...);  

        return sessionManager;  

    }  

    // ...  

}

2. 配置密码加密

 

在配置Realm时,可以定义一个HashedCredentialsMatcher属性,用于密码的加密和验证。例如:

 

@Bean  

public HashedCredentialsMatcher hashedCredentialsMatcher() {  

    HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();  

    hashedCredentialsMatcher.setHashAlgorithmName("SHA-256"); // 设置哈希算法  

    hashedCredentialsMatcher.setHashIterations(1024); // 设置哈希迭代次数  

    return hashedCredentialsMatcher;  

}  

  

@Bean  

public SecurityManager securityManager(CustomRealm customRealm) {  

    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();  

    securityManager.setRealm(customRealm);  

    securityManager.setCredentialsMatcher(hashedCredentialsMatcher()); // 设置密码加密验证器  

    return securityManager;  

}

3. 自定义Realm

 

在自定义Realm中,可以实现doGetAuthenticationInfo方法,用于获取用户的认证信息,包括密码和盐值等。例如:

 

public class CustomRealm extends AuthorizingRealm {  

    // ...  

    @Override  

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {  

        UsernamePasswordToken upToken = (UsernamePasswordToken) token;  

        String username = upToken.getUsername();  

        // 查询用户信息,包括密码和盐值等  

        // User user = userService.findByUsername(username);  

          

        // 假设从数据库中查询到的密码和盐值  

        String password = "e10adc3949ba59abbe56e057f20f883e"; // md5(123456)  

        String salt = "lilei"; // 假设这个盐值是从数据库中查出的  

          

        ByteSource credentialsSalt = ByteSource.Util.bytes(salt);  

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, credentialsSalt, this.getName());  

        return info;  

    }  

    // ...  

}

 

 

 

你可能感兴趣的:(后端)