SpringBoot 集成 JWT

JWT是啥?

网上各种详细介绍.
个人理解就是后台验证,后台检查请求是否合法
SpringBoot这一块的应用的话,就是通过filter拦截请求,进行验证,通过继续,不通过就拒绝请求

springboot 集成 JWT

第一步:引入jar包

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

第二步:编写创建token和解析token的工具

说一下token的解析,
因为jwt的规范是分三部分,第一部分包含token类型和数据加密方式
所以token在解析数据时可以直接获取数据,如下方法,所以尽量不要在token中添加敏感数据
如下,直接可以获取数据,不需要加密密码

DecodedJWT jwt = JWT.decode(token);
jwt.getClaim( yourkey ).asString();

所以能通过token获取数据,不代表token是合法的
所以jwt内部实现的验证,其实是将token中的数据用自定义的密码重新加密一遍,然后跟请求传过来的token中的第三段签名进行对比,如果一致并且没过期,则通过验证,否则失败


public class JwtUtils
{
    // 自定义token有效时间
    private static final long EXPIRE_TIME = 120 * 60 * 1000;
    
    // 自定义写进token的key
    private static final String CLAIM_NAME = "username";
    private static final String CLAIM_OPENID = "pwd";

    /**
     * 创建token
     * @param username
     * @param password
     * @return
     */
    public static String createToken(String username, String password)
    {
        return createToken(username, password, EXPIRE_TIME);
    }

    public static String createToken(String username, String pwd, long expireTime)
    {
        // 计算过期时间
        Date date = new Date(System.currentTimeMillis() + expireTime);
        // 加密处理密码,该密码自定义即可,用于校验token第三段的签名
        Algorithm algorithm = Algorithm.HMAC256("xyz123");
        return JWT.create()
                .withClaim(CLAIM_NAME, username)
                .withClaim(CLAIM_OPENID, pwd)
                .withExpiresAt(date)
                .sign(algorithm);
    }

    /**
     * 验证token
     * @param dbPwd
     * @param token
     */
    public static void verify(String token)
    {
        Algorithm algorithm = Algorithm.HMAC256("xyz123");
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try
        {
            jwtVerifier.verify(token);
        	// 校验方法返回的也是 DecodedJWT 实例,可以直接通过实例获取token中的数据
            // DecodedJWT decode = jwtVerifier.verify(token);
            // decode.getClaim(CLAIM_NAME).asString();
        }
        catch (TokenExpiredException e)
        {
            throw new TokenExpiredException("token已过期");
        }
        catch (JWTVerificationException e)
        {
            throw new JWTVerificationException("token验证失败");
        }
    }

	/**
	 *  这个方法建议自行修改,因为token中的key值是定义好的,这里直接写成获取指定key比较好
	 *  或者自己定义一个token类,定义好属性,直接返回一个实例
	 */
    public static String getClaim(String key)
    {
    	try
        {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim(key).asString();
        }
        catch (JWTDecodeException e)
        {
            throw new JWTVerificationException("token验证失败");
        }
    }
}

第三步:编写filter,以下是摘自若依框架的实现,自己做了一些修改

如何自定义一个拦截器这里就不多说了,不同的拦截器抽象类没细研究过,

public class JwtFilter extends AccessControlFilter
{
    private static final Logger LOGGER = LoggerFactory.getLogger(JwtFilter.class);
    
    // 这里定义了从request中获取token的方式,直接获取header中的key为token的值,
    // 这就要求前端发送请求时将令牌字符串放在header中的jwttoken
    private static final String AUTHZ_HEADER = "jwttoken";
    private final ThreadLocal<String> MSG_HOLDER = new ThreadLocal<>();

    @Override
    public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception
    {
        return super.onPreHandle(request, response, mappedValue);
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception
    {
        return this.executeLogin(request, response);
    }

    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception
    {
    	// 若依集成了shiro框架,这个WebUtils是shiro框架中的方法
        String token = WebUtils.toHttp(request).getHeader(AUTHZ_HEADER);
    	// 没有集成shiro可以用如下方法获取
        // HttpServletRequest hreq = (HttpServletRequest)request;
        // String token = hreq.getHeader(AUTHZ_HEADER);
        if (StringUtils.isEmpty(token))
        {
            MSG_HOLDER.set("消息头不正确,header需要携带token参数");
            return false;
        }
        try
        {
            // 这里封装了一个验证方法,token验证的核心就在这里,这里不报异常就是OK了
            JwtUtils.verify(token);
            return true;
        }
        catch (AuthenticationException e)
        {
            if (e.getCause() instanceof TokenExpiredException)
            {
                MSG_HOLDER.set("token已过期");
            }
            else if (e.getCause() instanceof JWTVerificationException)
            {
                MSG_HOLDER.set("用户密码错误");
            }
            else
            {
                MSG_HOLDER.set("用户信息验证失败:" + e.getMessage());
            }
            return false;
        }
    }

    /**
     * 请求前处理,处理跨域
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception
    {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        // 跨域时,option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name()))
        {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

    /**
     * 异常处理
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception
    {
        this.jwtFail(request, response, 401, "对不起,您无权限进行操作!");
        return false;
    }

    /**
     * 认证失败,异常返回
     */
    protected void jwtFail(ServletRequest request, ServletResponse response, int code, String message)
    {
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
        String contentType = "application/json;charset=UTF-8";
//        httpResponse.setStatus(401); 这里若依实现时返回401,我这边按自己前端需要,使用默认200
        httpResponse.setContentType(contentType);
        try
        {
            String msg = StringUtils.isNotEmpty(MSG_HOLDER.get()) ? MSG_HOLDER.get() : message;
            AjaxResult ajaxResult = AjaxResult.error().put(AjaxResult.CODE_TAG, code).put(AjaxResult.MSG_TAG, msg);
            PrintWriter printWriter = httpResponse.getWriter();
            printWriter.append(JSON.toJSONString(ajaxResult));
        }
        catch (IOException e)
        {
            LOGGER.error("sendChallenge error,can not resolve httpServletResponse");
        }
    }
}

第四部:使用filter

这个看你的框架怎么添加拦截器了
逻辑就是
1.实例化拦截器
2.添加到拦截器列表中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值