五分钟SpringBoot、JWT最佳实践

JWT简介

JWT的全称叫Json Web Token。JSON Web Token是一个开放标准(RFC 7519),该Token被设计为紧凑且安全的,用于在各方之间作为JSON对象安全地传输信息。由于信息是经过数字签名的,因此可以进行验证和信任。客户端只需使用一次凭据即可向服务器进行身份验证。在此期间,服务器验证凭据,并向客户端返回JWT。对于以后的所有请求,客户端可以使用这个Token向服务器进行身份验证,就不需要发送用户名和密码之类的凭据。

JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:Header、Payload、Signature。
在这里插入图片描述
Header:描述了JWT元数据,是一个 JSON 对象,它的格式如下:

{"alg":"HS256","typ":"JWT"}

alg属性表示签名所使用的算法,通常直接使用 HMAC SHA256。typ 属性表示令牌类型,这里就是 JWT。

Payload:就是存放有效信息的地方。这些有效信息包含以下三个部分

  1. 标准中注册的声明
    ss: jwt签发者
    sub: jwt所面向的用户
    aud: 接收jwt的一方
    exp: jwt的过期时间,这个过期时间必须要大于签发时间
    nbf: 定义在什么时间之前,该jwt都是不可用的.
    iat: jwt的签发时间
    jti: jwt的唯一身份标识

  2. 公共的声明
    在这里我们可以添加任何信息。

  3. 私有的声明
    私有声明是服务器和客户端共同定义的声明

Signature:这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密。

最终形成了如下字符。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTQ4Nzk2OTksInVzZXJuYW1lIjoiYSJ9.OYmhA25LE8Ujq3U8FYTG72PptbqglkAYBacnv3kjuj4"

下面是一张具体处理的流程图。
在这里插入图片描述

Spring Boot整合JWT

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

首先在application.properties中配置一个secret。

jwt.secret=abcdplokiahsfgdma
@Configuration
public class JwtConfig {

    @Value("${jwt.secret}")
    private String secret;

    public String getSecret() {
        return secret;
    }

    public void setSecret(String secret) {
        this.secret = secret;
    }
}

Controller

package com.hxl.springbootdemo.controller;

import com.hxl.springbootdemo.config.JwtConfig;
import com.hxl.springbootdemo.entity.R;
import com.hxl.springbootdemo.entity.User;
import com.hxl.springbootdemo.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @Autowired
    JwtConfig jwtConfig;


    @PostMapping(value="/login")
    @ResponseBody
    public R login(String username, String password){

        /**
         * 省略判断用户名密码
         */
        User user = new User(username,password);
        String token = JwtUtils.sign(user,jwtConfig);
        return  new R(0,"登录成功",token);
    }
    @PostMapping(value="/list")
    @ResponseBody
    public R list(User user){
        System.out.println(user);
        return  new R(0,"登录成功","data");

    }
}

创建一个拦截器,拦截没有登录的用户。

package com.hxl.springbootdemo.interceptors;

import com.auth0.jwt.interfaces.Claim;
import com.hxl.springbootdemo.config.JwtConfig;
import com.hxl.springbootdemo.exts.SystemException;
import com.hxl.springbootdemo.utils.JwtUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

public class AuthenticationInterceptor implements HandlerInterceptor {
    private Logger logger = LoggerFactory.getLogger(AuthenticationInterceptor.class);

    @Autowired
    JwtConfig jwtConfig;


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");

        if (token==null || token.length()==0){
            throw  new SystemException("未登录");
        }

        logger.info("认证{}",token);
        Map<String, Claim> verify = JwtUtils.verify(token, jwtConfig);
        if (verify==null){
            throw  new SystemException("用户身份验证失败");
        }
        return true;
    }
}

JwtUtils用来创建JWT和验证。


package com.hxl.springbootdemo.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.hxl.springbootdemo.config.JwtConfig;
import com.hxl.springbootdemo.entity.User;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.Map;

public class JwtUtils {
    public static String sign(User user, JwtConfig jwtConfig){

        String token = null;
        try {
            LocalDate localDate =LocalDate.now();
            localDate.plusDays(1);
            Date expir = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
            token = JWT.create()
                    /**
                     * 用户信息
                     */
                    .withClaim("username", user.getName())
                    /**
                     * 过期时间
                     */
                    .withExpiresAt(expir)
                    /**
                     * 签名
                     */
                    .sign(Algorithm.HMAC256(jwtConfig.getSecret()));
        } catch (Exception e){
            e.printStackTrace();
        }
        return token;

    }
    public static Map<String, Claim> verify(String token, JwtConfig jwtConfig){

        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(jwtConfig.getSecret())).build();

            DecodedJWT jwt = verifier.verify(token);

            return  jwt.getClaims();
        } catch (Exception e){
            return null;
        }

    }

}

自定义一个参数转换器。

package com.hxl.springbootdemo.handler;

import com.auth0.jwt.interfaces.Claim;
import com.hxl.springbootdemo.config.JwtConfig;
import com.hxl.springbootdemo.entity.User;
import com.hxl.springbootdemo.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.util.Map;

public class UserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
    @Autowired
    JwtConfig config;

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return  methodParameter.getParameterType().equals(User.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        String token = nativeWebRequest.getHeader("token");
        Map<String, Claim> verify = JwtUtils.verify(token, config);
        return new User(verify.get("username").asString(),"");
    }
}

配置拦截器和参数转换器。

package com.hxl.springbootdemo.config;

import com.hxl.springbootdemo.handler.UserHandlerMethodArgumentResolver;
import com.hxl.springbootdemo.interceptors.AuthenticationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import java.util.List;

@Configuration
public class WebConfig  extends WebMvcConfigurationSupport {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(createAuthenticationInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/login");
    }

    @Bean
    public  AuthenticationInterceptor createAuthenticationInterceptor(){
        return  new AuthenticationInterceptor();
    }

    @Bean
    UserHandlerMethodArgumentResolver createUserHandlerMethodArgumentResolver(){
        return  new UserHandlerMethodArgumentResolver();
    }
    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(createUserHandlerMethodArgumentResolver());
    }
}

全局异常处理。

@RestControllerAdvice
public class ErrorController {

    @ExceptionHandler(Exception.class)
    public R handlerException(Exception e){
        return  new R(-1,e.getMessage(),"{}");
    }
}

运行效果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值