Shiro会话管理和加密

会话管理

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

接下来我们从几个方面了解一下Shiro的会话管理。

会话相关的API

  • Subject.getSession():获取会话
  • session.setAttribute(key,val):设置会话属性
  • session.getAttribute(key):获取会话属性
  • session.removeAttribute(key):删除会话属性

SessionDAO

shiro提供SessionDAO用于会话持久化,提供CRUD操作。
在这里插入图片描述

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

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

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

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

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

缓存

具体实现

  1. 添加依赖
<dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>3.1.0</version>
</dependency>
  1. application.properties配置文件中添加Redis
spring.redis.port=6379
spring.redis.host=localhost
spring.redis.password=8261
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.pool.max-idle=8
spring.redis.timeout=5000
  1. 改造ShiroConfig
@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;

    @Resource
    private IRoleService roleService;

    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor attributeSourceAdvisor(){
        AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager());
        return advisor;
    }

    public RedisManager redisManager(){
        RedisManager redisManager=new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        redisManager.setTimeout(timeout);
        redisManager.setPassword(password);
        return redisManager;
    }

    public RedisCacheManager cacheManager(){
        RedisCacheManager cacheManager=new RedisCacheManager();
        cacheManager.setRedisManager(redisManager());
        cacheManager.setPrincipalIdFieldName("userName");
        cacheManager.setExpire(1800);
        return cacheManager;
    }
    //会话操作
    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 MyShiroRealm myShiroRealm(){
        MyShiroRealm shiroRealm=new MyShiroRealm();
        //启动缓存,设置缓存名称
        shiroRealm.setCachingEnabled(true);
        shiroRealm.setAuthorizationCachingEnabled(true);
        shiroRealm.setAuthorizationCacheName("authorizationCache");
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return shiroRealm;
    }

    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher=new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        hashedCredentialsMatcher.setHashIterations(3);
        return hashedCredentialsMatcher;
    }


    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        //注入Realm
        securityManager.setRealm(myShiroRealm());
        //注入缓存管理器
        securityManager.setCacheManager(cacheManager());
        //注入会话管理器
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }
    @Bean//Shiro过滤
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactory=new ShiroFilterFactoryBean();
        shiroFilterFactory.setSecurityManager(securityManager);
        shiroFilterFactory.setLoginUrl("/user/login");
        shiroFilterFactory.setSuccessUrl("/main");
        shiroFilterFactory.setUnauthorizedUrl("/403");
        Map<String,String> fileMap=new LinkedHashMap<String,String>();
        fileMap.put("/css/**","anon");
        fileMap.put("/fonts/**","anon");
        fileMap.put("/images/**","anon");
        fileMap.put("/js/**","anon");
        fileMap.put("/localcss/**","anon");
        fileMap.put("/localjs/**","anon");
        fileMap.put("/user/dologin","anon");
        fileMap.put("/user/logout","logout");
        //静态授权
//        fileMap.put("/user/list","perms[用户列表]");
//        fileMap.put("/user/add","perms[用户添加]");
//        fileMap.put("/user/edit/**","perms[用户编辑]");
//        fileMap.put("/user/del/**","perms[用户删除]");

        //动态授权
        List<Right> rights=roleService.findAllRights();
        for (Right right:rights){
            if (right.getRightUrl()!=null && !right.getRightUrl().trim().equals("")){
                fileMap.put(right.getRightUrl(),"perms["+right.getRightCode()+"]");
            }
        }

        fileMap.put("/**","authc");
        shiroFilterFactory.setFilterChainDefinitionMap(fileMap);
        return shiroFilterFactory;
    }
}

加密

哈希和盐

特点:

  1. 原始密码经过哈希函数计算后得到一个哈希值
  2. 改变原始密码,哈希函数计算出的哈希值也会相应改变
  3. 同样的密码,哈希值也是相同的
@Test
    public void testMd5Hash(){
        User user=new User("MyBatis-Plus","aaaaaa",null,null);
        Md5Hash md5Hash=new Md5Hash(user.getUserPassword(),"",1);
        String md5Salt=md5Hash.toString();
        Md5Hash md5=new Md5Hash(user.getUserPassword(),md5Salt,3);
        System.out.println(md5.toString());
    }

加密和验证

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

具体实现

SimpleCredentialsMatcher

  1. 在UserService中添加加密方法
 @Override
    public String enctyptPassword(Object plaintextPassword) throws IllegalArgumentException{
        String salt=“czkt”;
        Md5Hash md5Hash=new Md5Hash(plaintextPassword,salt,2);
        return md5.toString();
    }
  1. 修改IndexController中的login方法
 @RequestMapping("/user/dologin")
    public String dologin(String userName, String userPassword, Model model, HttpSession session){
        try {
        	 userPassword=userService.enctyptPassword(userPassword);
            Subject subject= SecurityUtils.getSubject();
            UsernamePasswordToken token=new UsernamePasswordToken(userName,userPassword);
            subject.login(token);
            //.....
            }
}

HashedCredentialsMatcher

  1. 在配置类中创建HashedCredentialsMatcher并注入MyShiroRealm:
 @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher=new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        hashedCredentialsMatcher.setHashIterations(3);
        return hashedCredentialsMatcher;
    }
  1. 修改MyShiroRealm对象中的代码
 @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("调用MyShiroRealm.doGetAuthenticationInfo获取身份信息");
        UsernamePasswordToken token=(UsernamePasswordToken)authenticationToken;
        String userName=token.getUsername();
        User user=userService.getUserByUserName(userName);
        if (user==null){
          throw new UnknownAccountException("账号错误");//账号错误
        }
        if (user.getUserFlag()==null || user.getUserFlag().intValue()==0){
            throw  new LockedAccountException("账号已锁定");//账号锁定
        }
        SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(user,
                user.getUserPassword(),
                ByteSource.Util.bytes(“czkt”),
                getName());
        return info;
    }

登录次数限制

  1. 添加spring-boot-starter-data-redis依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 修改MyShiroRealm注入stringRedisTemplate,在doGetAuthenticationInfo控制登录次数
@Resource
    private IUserService userService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    private String SHIRO_LOGIN_COUNT="shiro_login_count_";
    private String SHIRO_IS_LOCK="shiro_is_lock_";

 @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("调用MyShiroRealm.doGetAuthenticationInfo获取身份信息");
        UsernamePasswordToken token=(UsernamePasswordToken)authenticationToken;
        String userName=token.getUsername();
        ValueOperations<String,String> operations=stringRedisTemplate.opsForValue();
        operations.increment(SHIRO_LOGIN_COUNT+userName,1);
        if (Integer.parseInt(operations.get(SHIRO_LOGIN_COUNT+userName))>3){
            operations.set(SHIRO_IS_LOCK+userName,"LOCK",1, TimeUnit.HOURS);
            //stringRedisTemplate.expire(SHIRO_IS_LOCK+userName,1, TimeUnit.HOURS);
            stringRedisTemplate.delete(SHIRO_LOGIN_COUNT+userName);
        }
        if ("LOCK".equals(operations.get(SHIRO_IS_LOCK+userName))){
            throw new LockedAccountException();
        }
        User user=userService.getUserByUserName(userName);
        if (user==null){
          throw new UnknownAccountException("账号错误");//账号错误
        }
        if (user.getUserFlag()==null || user.getUserFlag().intValue()==0){
            throw  new LockedAccountException("账号已锁定");//账号锁定
        }
        Md5Hash md5Hash=new Md5Hash(new String(((UsernamePasswordToken)token).getPassword()),"",1);
        SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(user,
                user.getUserPassword(),
                ByteSource.Util.bytes(md5Hash.toString()),
                getName());
        return info;
    }
  1. 修改IndexController注入stringRedisTemplate,在登录方法中登录成功时清空
@RequestMapping("/user/dologin")
    public String dologin(String userName, String userPassword, Model model, HttpSession session){
        try {
            Subject subject= SecurityUtils.getSubject();
            UsernamePasswordToken token=new UsernamePasswordToken(userName,userPassword);
            subject.login(token);
            stringRedisTemplate.delete(SHIRO_LOGIN_COUNT+userName);
            User user=(User) subject.getPrincipal();
            //省略其他代码..
            }
     }
  1. 测试:
    在这里插入图片描述
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值