SpringBoot AOP 切面 使用 JWT 完成登录鉴权

最近项目上碰到一个问题,使用SpringBoot拦截器实现 JWT登录鉴权时,无法Pass swagger2的请求,经过研究最终解决了该问题,方案如下,如有更好的建议,请大家不吝赐教。

目的:

使用JWT进行登录,使用前后端分离的token鉴权。

但是swagger2的文档在开发阶段需要放行,不经过权限认证。

切面鉴权失败后抛出异常,使用SpringBoot的全局异常处理器统一处理,返回到客户端。

如果碰到问题,请私信我。

TokenUtil实现JWT封装工具


import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;

public class TokenUtil {

    /**
     * 过期时间,120分钟
     */
    private static final long EXPIRE_TIME = 120 * 60 * 1000;
    private static final String TOKEN_SECRET = "@#$%#$%";  //密钥
    public static final String TOKEN = "token";

    /**
     * 签名生成
     * @param userName 用户名
     * @return token
     */
    public static String sign(String userName) {
        return JWT.create()
                .withSubject(userName)
                .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRE_TIME))
                // 使用了HMAC256加密算法。
                .sign(Algorithm.HMAC256(TOKEN_SECRET));
    }

    /**
     * 签名验证
     *
     * @param token token
     * @return userName
     */
    public static String verify(String token) {
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET))
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            if (jwt != null) {
                return jwt.getSubject();
            } else {
                return null;
            }
        } catch (TokenExpiredException e) {
            throw new UnTokenException("Token expired.");
        } catch (Exception e) {
            throw new UnTokenException("Token error.");
        }
    }

    /**
     * 检查 token 是否需要更新
     *
     * @param token token 值
     * @return 过期时间
     */
    public static boolean isNeedUpdate(String token) {
        // 获取 token 过期时间
        try {
            Date expiresAt = JWT.require(Algorithm.HMAC256(TOKEN_SECRET))
                    .build()
                    .verify(token)
                    .getExpiresAt();
            // 时间过去一半之后更新token
            return (expiresAt.getTime() - System.currentTimeMillis()) < (EXPIRE_TIME >> 1);
        } catch (TokenExpiredException e) {
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
TokenAuthenticationAspect只控制 SpringBoot的Controller
/**
 * Token鉴权切面
 */
@Slf4j
@Aspect
@Component
public class TokenAuthenticationAspect {

    /**
     * 登录路径 ->放行
     */
    private static final String LOGIN = "/user/login";

    /**
     * 声明切入点表达式。
     */
    @Pointcut("execution(public * com.kczl.swarm.controller..*..*(..))")
    public void pointcut() {
    }

    /**
     * 环绕通知
     * 可以理解为对这个方法进行环绕通知
     * ProceedingJoinPoint 参数 用于环绕通知,
     * 使用proceed()方法来执行目标方法,可以理解为 前置通知结束 开始执行使用该注解的方法。
     */
    @Around("pointcut()")
    public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String token = request.getHeader(TokenUtil.TOKEN);
        if (request.getRequestURI().equals(LOGIN)) {
            return joinPoint.proceed();
        }

        if (StringUtils.isNotBlank(token)) {
            String sub = TokenUtil.verify(token);
            if (StringUtils.isBlank(sub)) {
                throw new UnTokenException("Token is invalidate.");
            }
            if (TokenUtil.isNeedUpdate(token)) {
                String newToken = TokenUtil.sign(sub);
                attributes.getResponse().setHeader(TokenUtil.TOKEN, newToken);
            }
        } else {
            throw new UnTokenException("Not login.");
        }
        //执行业务逻辑,放行
        return joinPoint.proceed();
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值