shiro使用(使用token)

既然要做,就做的细致一点,对得起自己!

为什么使用token,请看这里

解决思路

1.增加一个过滤器,对每一个请求都进行token的校验。如果为登录请求,则可以放过。

2.将过滤器设置进shiro中。

第一步:实现自定义ShiroFilter.这里选择继承FormAuthenticationFilter,至于为什么不继承AuthenticatingFilter,后面会解释。

从当前shiro中取出User信息,进行加密,将加密结果和客户端传过来的token比较,来判断token的合法性、有效性。

public class ShiroFilter extends FormAuthenticationFilter {

    //加密的字符串,相当于签名
    private static final String SINGNATURE_TOKEN = "加密token";

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        //这里只有返回false才会执行onAccessDenied方法,因为
       // return super.isAccessAllowed(request, response, mappedValue);
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {

        String token = getRequestToken((HttpServletRequest) request);
        String login = ((HttpServletRequest) request).getServletPath();

        //如果为登录,就放行
        if ("/user/login".equals(login)){
            return true;
        }
        if (StringUtils.isBlank(token)){
            System.out.println("没有token");
            return false;
        }

        //从当前shiro中获得用户信息
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        //对当前ID进行SHA256加密
        String encryptionKey= DigestUtils.sha256Hex(SINGNATURE_TOKEN+user.getName());
        if (encryptionKey.equals(token)){
            return true;
        }else{
          System.out.println("无效token");
        }
        return false;
    }
    private String getRequestToken(HttpServletRequest request){
        //默认从请求头中获得token
        String token = request.getHeader("token");
        //如果header中不存在token,则从参数中获取token
        if(StringUtils.isBlank(token)){
            token = request.getParameter("token");
        }
        return token;
    }
}

说明:

1.为什么这里重写isAccessAllowed要直接返回false?查看源码你会发现,只有shiro中Filter最终会执行AccessControlFilter这个类下面的onPreHandle这个方法,而这个方法就一句代码,如下:

我们所有的token校验方法都是在onAccessDenied里进行的,而且这里还是一个或判断,一旦isAccessAllowed为 true,那就不会执行onAccessDenied,稍微懂点与或非的知识就能理解吧。

2.为什么要把主要token验证方法写在onAccessDenied里,而不是写在isAccessAllowed,isAccessAllowed也是可以返回false/true啊?上面的截图源码已经很清楚了,如果在isAccessAllowed里校验false/true,在token校验失败之后,依然还是会执行onAccessDenied这个方法,这个方法如下图:

可以看到,如果所有token验证失败,我们直接把结果返回给前台就可以了,没有必要再进行一次无效的验证。同时,在isAccessAllowed方法里,shiro做的判断很粗,他只是判断了一下,用户是否已经登录,而通过之后的所有请求都是合法的,这显然不符合我们的要求。源码如下

登录之后,这里标红的地方就会为true。所以基于以上两点,我们的代码是第一步那样写。

第二步:当用户登录成功之后,也就是subkect.login(token)校验通过之后,我们会把token返回给客户端,以便于下次请求时进行token的验证。

try{
            subject.login(token);
            //返回的token
            String encryptionKey= DigestUtils.sha256Hex(SINGNATURE_TOKEN+user.getName());
        }catch (Exception e){
            System.out.println("对具体异常处理");
        }

第三步:让shiro执行我们自定义的过滤器。

除了不需要认证权限的操作,统一都由自定义的ShiroFilter去处理。

以上三步完成之后,就可以对所有需要鉴权的请求进行处理。省去了服务器内存和数据库存储和查询的消耗

备注:以上实现加密方式是基于Apache.common.codec下的sha256加密的方法加密的,但是没有对应解密的方法,所以说,上篇文章提到的时间戳鉴定token过期时间就实现不了,目前上面的代码是没有加过期限制的。解决方法也很简单,你只需要使用Base64加密算法就可以,这是可以解密的,可以将时间戳加密到token里,然后再解密,校验token是否过期

思考:在分布式系统中,如何保证多个服务之间可以使用一份token?自己简单的想法是这样的,可以参考  推送/订阅 这种方式可以有一个单独的token的认证服务器,负责token的创建、销毁、校验等。一旦token合法则把token返回给客户端,然后客户端拿着这个token去请求不同的服务A,B,C等。

不过好像有一个JWT提供的token也很厉害,目前还没有研究,懂的大神可以留言教下我,感激不尽!

 本专栏并没有提供相关完整的代码,不过,有一个前后端项目中整合了Shiro,如有需要,代码在这里

  • 20
    点赞
  • 116
    收藏
    觉得还不错? 一键收藏
  • 19
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值