利用JWT、注解拦截请求并验证token

108 阅读3分钟

在Web应用程序中,注解是一种方便的方式来拦截和处理请求。可以通过在代码中添加注解来定义一些特殊的行为,比如拦截器(Interceptor)等。 拦截器是一种处理请求的技术,它可以在请求被处理之前或之后对请求进行处理。通过使用拦截器,可以在处理请求之前或之后添加额外的逻辑,例如身份验证、权限检查等。

所需JWT依赖:

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

定义一个登录用户类:

/**
 * @Author RainCity
 * @Date 2021-05-24 10:49:59
 * @Desc 登录用户
 */
public class LoginUser implements Serializable {
    private static final long serialVersionUID = 768788313972112861L;

    /**登录名*/
    private String loginName;
    /**登录密码*/
    private String password;

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    
    @Override
    public String toString() {
        return "LoginUser {" +
                "loginName='" + loginName + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

利用JWT生成token:

/**
 * @Author RainCity
 * @Date 2021-05-24 11:47:04
 * @Desc jwt 根据当前登录人生成token
 */
public class JsonWebToken {
    private static final String ISSUER="ISSUER";

    /**
     * 生成token---有过期时间
     * @param loginName 登录名(唯一标识)
     * @param password 登陆密码
     * @param second 多少秒后过期
     * @return {@link String}
     * @version v1.0.0
     */
    public static String createToken(String loginName, String password, int second) {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.SECOND, second);
        return JWT.create()
                .withJWTId("jwt_id")
                //签发者
                .withIssuer(ISSUER)
                //将 登录名 保存到token里面
                .withAudience(loginName)
                //签发时间
                .withIssuedAt(new Date())
                //过期时间
                .withExpiresAt(cal.getTime())
                //以 password 作为token的密钥
                .sign(Algorithm.HMAC256(password));
    }
	
	/**
     * 生成token---无过期时间
     * @param loginName 登录名(唯一标识)
     * @param password 登陆密码
     * @return {@link String}
     * @version v1.0.0
     */
    public static String createToken(String loginName, String password) {
        return JWT.create()
                .withJWTId("jwt_id")
                //签发者
                .withIssuer(ISSUER)
                //将 登录名 保存到token里面
                .withAudience(loginName)
                //签发时间
                .withIssuedAt(new Date())
                //过期时间
                //.withExpiresAt(cal.getTime())
                //以 loginName+password 作为token的密钥
                .sign(Algorithm.HMAC256(loginName+password));
    }
}

注解:

/**
 * @Author RainCity
 * @Date 2021-05-24 10:25:39
 * @Desc 在需要验证的方法上使用此注解
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthRequired {
    boolean required() default true;
}

请求拦截器:

/**
 * @Author RainCity
 * @Date 2021-05-24 10:39:24
 * @Desc
 */
@SuppressWarnings(value = {"rawtypes" })
public class AuthenticationInterceptor implements AsyncHandlerInterceptor {

    @Resource
    public RedisTemplate redisTemplate;

    private static final String INVALID_MSG ="登录失效,请重新登录";
    private static final String NOT_EXIST_MSG ="用户不存在,请重新登录";

	private static final String LOGIN_KEY = "CUS_LOGIN:";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        // 判断接口是否需要登录
        AuthRequired methodAnnotation = method.getAnnotation(AuthRequired.class);
        // 有 @AuthRequired 注解,需要认证
        if(null != methodAnnotation){
            // 执行认证
            // 从 http 请求头中取出 token
            String token = request.getHeader("token");
            Assert.notNull(token, "未登录");
            String loginName;
            try {
                // 获取 token 中的 账号
                loginName = JWT.decode(token).getAudience().get(0);
                if(StringUtils.isEmpty(loginName)){
                    throw new RuntimeException(INVALID_MSG);
                }
                LoginUser loginUser = (LoginUser) redisTemplate.opsForValue().get(LOGIN_KEY + loginName);
                if (null == loginUser) {
                    throw new RuntimeException(NOT_EXIST_MSG);
                }
                // 验证 token
                JWTVerifier verifier = JWT.require(Algorithm.HMAC256(loginUser.getLoginName()+loginUser.getPassword())).build();
                verifier.verify(token);
                return true;
            } catch (JWTVerificationException e) {
                throw new RuntimeException("身份认证失败");
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) {

    }
}

注解拦截请求是一种优雅而灵活的方式,可以帮助我们更好地管理和处理请求。