Shiro与JwtToken的整合使用

216 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

1、概述

上一篇的分享中,我们整合完 Shiro 是用的 Shiro 自己的 Session 实现用户的认证的。Session 是一种有状态的认证方式,服务端需要保存用户的状态、客户端请求依赖服务端。而 jwt 则是一种常见的无状态认证方式。

服务端不保存客户端的请求信息,在客户端进行第一次请求时,对用户信息进行认证,通过后将加密token返回给客户端,作为客户端的访问凭证。这就是无状态登录的一个主要流程。

JWT - Json Web Token,是一个开放标准(RFC7519),一个轻量级的JSON风格授权和身份认证规范。它主要由 Header、payload、signature 三部分组成。其中 Header 是头部信息,包括签名算法等信息;payload 包括用户信息、token的签发时间、签发人等;signature就是这个token的签名,用来校验token是否合法。

2、Shiro + Jwt 整合使用

核心就是将Shiro禁用自带的Session,使用我们自己生成的JwtToken进行认证、授权。

2.1、引入依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.12.1</version>
</dependency>

2.2、改造Shiro配置类

//1、禁用Shiro自带的Session
@Bean("securityManager")
public DefaultWebSecurityManager getManager(ShiroRealm  realm) {
    DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
    // 设置realm
    manager.setRealm(realm);

    /*
    * 关闭shiro自带的session
    */
    DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
    DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
    defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
    subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
    manager.setSubjectDAO(subjectDAO);
    return manager;
}

//2、在Shiro过滤器配置中增加JwtFilter
filterMap.put("jwt", new JWTFilter());

2.3、修改ShiroRealm

//1、修改认证逻辑,改为JwtToken
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
    String token = (String) auth.getCredentials();
    //token解密,得到用户信息
    TokenUserInfo tuInfo = JWTUtils.tokenInfoByToken(token); 
    String userId = tuInfo.getUserId();  
    SysUser user = getUserById(userId);
    String pwd = user.getPassword(); 
    if (!JWTUtils.verify(token, tuInfo, pwd)) {
        throw new AuthenticationException("用户名密码错误!");
    } 
    return new SimpleAuthenticationInfo(user, token, "jwt_token");
}

2.4、增加JwtFilter

public class JWTFilter extends BasicHttpAuthenticationFilter {  
    /**
     * 判断是否需要登录
     * @param request
     * @param response
     * @return
     */
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest) request;
        String authorization = req.getHeader(JWT.AUTH);
        return authorization != null;
    }

    /**
     * 执行login
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String authorization = httpServletRequest.getHeader(JWT.AUTH); 
        if (JWTUtils.loginExpire(authorization)) {
            throw new AuthenticationException(expMsg);
        }
        if (JWTUtils.needRefresh(authorization)) {
            authorization = refreshToken(request, response);
        }
        JWTToken token = new JWTToken(authorization);
        Subject subject = getSubject(request, response); 
        subject.login(token);
        return true;
    } 

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { 
        HttpServletRequest hRequest = (HttpServletRequest) request; 
        if (isLoginAttempt(request, response)) {
            try {
                boolean loginSuccess = executeLogin(request, response);
                return loginSuccess;
            } catch (Exception e) {
                LOGGER.error(e); 
            }
        }
        return false;
    }

    private String refreshToken(ServletRequest request, ServletResponse response) throws Exception { 
        String token = this.getAuthzHeader(request); 
        TokenUserInfo tuInfo = JWTUtils.tokenUserInfoByToken(token); 
        SysUser user = authUserService.getUserById(tuInfo.getUserId()); 
        String pwd = user.getPassword();
        Algorithm algorithm = Algorithm.HMAC256(pwd);
        JWTUtils.verifySign(algorithm, token);
        String newToken = JWTUtils.sign(tuInfo, pwd); 
        HttpServletResponse httpServletResponse = (HttpServletResponse) response; 
        httpServletResponse.addHeader("RefreshJwtToken", newToken);
        return newToken;
    }  

}

好了,到此我们就愉快的将Shiro自带Session换成了JwtToken了。😎