SpringBoot-整合Shiro和JWT时关于JWT部分的内容

本文提供一个SpringBoot中整合和shiro和JWT时,JWT的模板。

关于JWT(Json web token)的知识可以参考如下博客:
https://www.jianshu.com/p/576dbf44b2ae
https://zhuanlan.zhihu.com/p/86937325

SpringBoot中整合和shiro和JWT时,关于JWT的部分有三个类需要编写:

JWTToken: 实现AuthenticationToken
JWTUtils: 工具类,用于token的生成,验证,以及获取token中的一些信息
JWTFilter: 过滤器,判断请求中是否携带token,以便确定是否需要提交realm登录

1、JWTToken

import org.apache.shiro.authc.AuthenticationToken;

public class JWTToken implements AuthenticationToken {

    private String token;

    public JWTToken(String token) {
        this.token = token;
    }
    //getPrincipal()方法返回的是帐号,getCredentials()返回的是密码
    //在这里的实现里面全部都返回token

    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}

JWTToken实现AuthenticationToken的原因是在Shiro的Realm的doGetAuthenticationInfo(AuthenticationToken authenticationToken)方法中传入的是AuthenticationToken.

2、JWTUtils

该工具类在其他地方会被使用,比如在用户第一次登录时用sign方法生成token。以及用户已经登陆后,验证用户发送的token是否正确,可以用verify方法。

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.stereotype.Component;
import java.util.Date;

//JWTUtils:用来生成token,校验token,从token中获取登录名
@Component
public class JWTUtils {

    private static final long EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000;

    //生成token
    public static String sign(long userid,String password){
        try{
            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            Algorithm algorithm = Algorithm.HMAC256(password);//用password作为secret,当然这个password要用MD5加密后的
            return JWT.create()
                    .withClaim("userid", userid)//将userid信息保存到token中
                    .withExpiresAt(date) //设置过期token过期时间
                    .sign(algorithm); // 加密
        }catch(Exception e){
            return null;
        }
    }

    //校验token
    public static boolean verify(String token, long userid, String password){
        try {
            Algorithm algorithm = Algorithm.HMAC256(password);
            JWTVerifier jwtVerifier = JWT.require(algorithm)
                    .withClaim("userid", userid).build();
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
            return true;
        } catch (Exception e){
            return false;
        }
    }

    //从token中获取用户名
    public long getUserId(String token){
        if(token == null || "".equals(token)){
            return -1;
        }
        try {
            DecodedJWT jwt = JWT.decode(token); //解码token
            return jwt.getClaim("userid").asLong(); //获取token保存的userid信息
        } catch(Exception e){
            return -1;
        }
    }
}

3、JWTFilter

继承shiro中的BasicHttpAuthenticationFilter,该类会在ShiroConfig中被添加到filter中,以后每个请求都会经过JWTFilter中的isAccessAllowed方法

import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.stereotype.Component;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;

@Component
public class JWTFilter extends BasicHttpAuthenticationFilter  {
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object mappedValue) {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String token = request.getHeader("token"); //从前端传过来的request中取token
        if(token != null){ //判断用户是不是需要登录,如果token不为空表示需要登录
            try{
                //应该在这里判断token是否过期
                executeLogin(request, servletResponse);
                return true;
            }catch(Exception e){
                responseError(servletResponse, e.getMessage());
            }
        }
        //如果请求头不存在 Token,可能是游客状态访问或者一些不需要登陆就能访问的请求,无需检查 token,直接返回 true
        return true;
    }


    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader("token");
        JWTToken jwtToken = new JWTToken(token);
        // 提交给realm进行登入,如果错误他会抛出异常并被捕获
        getSubject(request, response).login(jwtToken);
        // 如果没有抛出异常则代表登入成功,返回true
        return true;
    }

    /**
     * 将非法请求跳转到 /unauthorized/**
     */
    private void responseError(ServletResponse response, String message) {
        try {
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;
            //设置编码,否则中文字符在重定向时会变为空字符串
            message = URLEncoder.encode(message, "UTF-8");
            httpServletResponse.sendRedirect("/unauthorized/" + message);
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}

原文地址:https://www.cnblogs.com/variablex/p/14513580.html