Shiro集成redis和JWT碰到的问题

对Shiro的使用,一般都会涉及到自定义验证身份的问题。那么就需要实现自己的 AuthorizingRealmAuthorizing是授权的意思, realm 有领域的意思,合起来大概就是自定义实现授权的地方了。

基本的使用教程本文就不讲了,网上有很大。我主要记下自己在集成redis和jwt过程中碰到的几个问题。

一、与redis集成后,redis没有生效的问题。

我们首先要明白redisshiro集成的目的是什么。

当然是管理session啊!使用redis管理session能够方便的实现session集群,并且在服务重启(或服务器重启)时不会丢失session,而且关键使用它来管理session比tomcat强太多了啊。

session的管理可以归为一种Cache,我们在shiro的配置中就要注意这点了。

关键代码,在ShiroConfig

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //设置realm
        securityManager.setRealm(myShiroRealm());
        securityManager.setCacheManager(cacheManager());  //这里放redis的实现
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * cacheManager 缓存 redis实现
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
//        redisManager.setTimeout(1800);
//        redisManager.setExpire(1800);// 配置缓存过期时间
//        redisManager.setTimeout(0);
        // redisManager.setPassword(password);
        return redisManager;
    }

/**
     * Session Manager
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        // 设置redis缓存过期时间
        redisSessionDAO.setExpire(86400);
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

我们使用Redis就是为了管理它的session的,这里有shiro-redis的开源插件可以使用。

设置redis的过期时间请注意shiro-redis的版本,最新版3.2.1是在RedisSessionDAO中设置过期时间的,老一些的版本(我不清楚具体什么版本开始改变的)是在RedisManager中设置的。

二、与JWT的集成

如果不用考虑App端调用服务器端的接口问题,可以忽视这个,但如果存在APP端跨域调用接口,并且需要shiro来管理认证时,那么使用Jwt就是个不错的选择了。

在App端使用session是比较麻烦的,而且各种终端的实现方式不一样,有H5的,有原生(Android、ios)的,都是要先拿到服务器端返回的sessionID,再把它塞进其它的请求headers中,安全问题不谈。

由于使用session比较麻烦,那么我只能考虑使用其它的token来解决身份验证的问题子,而JWT就是专门来干这个事的。JWT本身就可以替代session来做身份认证,但这里我已经用了shiro和redis,所以我仅用JWT来做一个加密令牌的工作。

这里有一套已经集成好的项目 Shiro基于SpringBoot +JWT搭建简单的restful服务

基本可以直接拿来即用了。

我讲这个,主要是因为这个项目中没有对密码的存储进行加盐加密处理,仅仅是对用户名和密码进行加密返回了token,在实际的项目中我们通常会对密码进行加密的,所以实现起来就和这个项目中有所不同,我仅截取一些与这个项目中的实现不同的代码。

首先是登录的地方:

User user = userService.selectByField("username", loginRbo.getUsername());
            SimpleHash encryptPwd = new SimpleHash("MD5", loginRbo.getPassword(), 		ByteSource.Util.bytes(user.getSalt()));
            if (!encryptPwd.toHex().equals(user.getPassword())) {
                return authorityFailed("用户名或密码不正确!");
            }
            Subject subject = SecurityUtils.getSubject();
            String token = JWTUtil.sign(user.getId(), user.getPassword());
            JWTToken jwtToken = new JWTToken(token);
            try {
                subject.login(jwtToken);
            } catch (AuthenticationException e) {
                System.out.println(e.getMessage());
                return authorityFailed("用户名或密码不正确!");
            }

我在这里对密码进行了MD5加盐加密,然后因为我的项目中是用redis来管理用户登录的,所以我需要用到subject.login()这个是与redis集成的关键操作。

注意这上面的代码中没有用username+password的组合加密,我用了userId+password,这个只是换了个字段,不过需要把对应的取值也换成userId。

有一个非常值得注意的地方,认证的匹配方式。

我因为已经对密码进行过加密,并且已经实现了除JWT之外的所有代码,所以我的ShiroConfig中关于Realm的配置是这样的:

    /**
     * 身份认证realm
     *
     * @return
     */
    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

这里我对MyshiroRealm设置的认证匹配方式是

  /**
     * 凭证匹配器
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

        hashedCredentialsMatcher.setHashAlgorithmName("MD5"); // 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(1); // 散列的次数,比如散列两次,相当于 md5(md5(""));

        return hashedCredentialsMatcher;
    }

但由于使用了JWT的加密后,匹配方式已经不是一个MD5的散列算法了。我如果不改这部分代码就会报一个。 java.lang.IllegalArgumentException: Odd number of characters.

反正就是他匹配不上了。如果有碰到这个问题,多半从匹配算法去找原因。

所以简单的解决方法就是不用这种匹配方式,直接用最简单的匹配方式,也就是不用它进行加、解密了。

那么直接删掉这

myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());

就可以了。有以上两个问题解决不了的,可以私密我。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值