Preface:
1. What is JWT? A: J (JSON) W (Web) T (token) is a JSON-based token used to declare an identity on the network.
2. How is JWT implemented? JWT is composed of three pieces of information. Use these three pieces of information. All of these are joined together to form a JWT string.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI1IiwiaWF0IjoxNjAwOTMxNDE3LCJleHAiOjE2MDA5MzUwMTd9.bOVLeigaKqYaOj90QKWigb tqtmaHGVR-dxKXP6jgEB8JdaP6ybImztrD8EfxhnmgmxMrOUodgJG_14DA9aWiUgCopy the code
Jwt is usually composed of three parts: The first part is header encryption algorithm HMAC SHA256, etc., declaration type Jwt;
header = '{"alg":"HS256","typ":"JWT"}'
Copy the code
Base64 encryption of the header forms the first part
Iss: JWT signer sub: JWT user aud: JWT receiver exp: expiration time of the JWT. The expiration time must be greater than the issuance time NBF: Defines the time before the JWT is unavailable. Iat: JWT issue time JTI: JWT unique identifier used as a one-time token to avoid replay attacks.
The third part of JWT is a visa information, which consists of three parts: Header 2. Payload 3. Secret The unsigned token is a combination of the base64URL-encoded header and the message body (separated by “.”). The signature is calculated using a private key:
key = 'secretkey'
unsignedToken = encodeBase64(header) + '. ' + encodeBase64(payload)
signature = HMAC-SHA256(key, unsignedToken)
Copy the code
JWT consists of three parts: JWT header + payload + signature finally, the base64URL-encoded signature (also separated by “.”) on the end of the unsigned token is JWT:
token = encodeBase64(header) + '. ' + encodeBase64(payload) + '. ' + encodeBase64(signature)
Copy the code
The above is a brief description of JWT. For further study, please visit the OFFICIAL website of JWT
3.JWT features ① three parts, each part of the transformation of the string ② decryption time did not use the database, only the use of secret for decryption ③ JWT secret must not leak!
SpringBoot integrates the Jwt code implementation process
1. Import JWT dependencies
<! -- JWT dependency -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
Copy the code
2. Application. Yml configuration
config:
jwt:
# Encryption key
secret: science123456
# Token validity duration in milliseconds
expire: 3600
# header name
header: token
Copy the code
3. JWT configuration class
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
/** * JWT token, case sensitive */
@ConfigurationProperties(prefix = "config.jwt")
@Component
public class JwtConfig {
private String secret;
private long expire;
private String header;
/** * Generate token *@param subject
* @return* /
public String createToken (String subject){
Date nowDate = new Date();
Date expireDate = new Date(nowDate.getTime() + expire * 1000);Expire = 3600 ms 3600 x 1000=1 hour
return Jwts.builder()
.setHeaderParam("typ"."JWT")
.setSubject(subject)
.setIssuedAt(nowDate)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
/** * Obtain the token registration information *@param token
* @return* /
public Claims getTokenClaim (String token) {
try {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}catch (Exception e){
// e.printStackTrace();
return null; }}/** * Verify whether the token is expired *@param expirationTime
* @return* /
public boolean isTokenExpired (Date expirationTime) {
return expirationTime.before(new Date());
}
/** * Obtain token expiration time *@param token
* @return* /
public Date getExpirationDateFromToken(String token) {
return getTokenClaim(token).getExpiration();
}
/** * get the user name from the token */
public String getUsernameFromToken(String token) {
return getTokenClaim(token).getSubject();
}
/** * get JWT release time */
public Date getIssuedAtDateFromToken(String token) {
return getTokenClaim(token).getIssuedAt();
}
// --------------------- getter & setter ---------------------
public String getSecret(a) {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public long getExpire(a) {
return expire;
}
public void setExpire(long expire) {
this.expire = expire;
}
public String getHeader(a) {
return header;
}
public void setHeader(String header) {
this.header = header; }}Copy the code
4. Configure global interception
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.SignatureException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** * Configure interceptor */
@Component
public class TokenInterceptor extends HandlerInterceptorAdapter {
@Resource
private JwtConfig jwtConfig ;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws SignatureException {
/** Address filtering does not block the login, but permits the login directly. If register also needs to permit the login, you can add the permit judgment */ in the following section
String uri = request.getRequestURI() ;
if (uri.contains("/login")) {return true ;
}
/** Token authentication */
String token = request.getHeader(jwtConfig.getHeader());
if(StringUtils.isEmpty(token)){
token = request.getParameter(jwtConfig.getHeader());
}
if(StringUtils.isEmpty(token)){
throw new SignatureException(jwtConfig.getHeader()+ "Can't be empty.");
}
Claims claims = null;
try{
claims = jwtConfig.getTokenClaim(token);
if(claims == null || jwtConfig.isTokenExpired(claims.getExpiration())){
throw new SignatureException(jwtConfig.getHeader() + "Invalid. Please log in again."); }}catch (Exception e){
throw new SignatureException(jwtConfig.getHeader() + "Invalid. Please log in again.");
}
/** Set identityId user ID */
request.setAttribute("identityId", claims.getSubject());
return true; }}Copy the code
Register interceptors with SpringMvc
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
/** * Register interceptors to SpringMvc */
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Resource
private TokenInterceptor tokenInterceptor ;
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor).addPathPatterns("/ * *"); }}Copy the code
5. Unify exception handling classes
import com.king.science.dto.Result;
import com.king.science.enums.ResponseResultEnum;
import io.jsonwebtoken.SignatureException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/** * write a unified exception handling class */
@RestControllerAdvice
public class PermissionHandler {
@ExceptionHandler(value = { SignatureException.class })
@ResponseBody
public Result authorizationException(SignatureException e){
returnResult.failed(ResponseResultEnum.EX_TOKEN_ERROR_CODE); }}Copy the code
6. Return the Result utility class
import com.king.science.enums.ResponseResultEnum;
import com.king.science.util.CommonConstant;
/** * Created by x on 2020/09/24. */
public class Result extends BizResult {
public static Result toResult(int code, String msg, Object object){
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
result.setData(object);
return result;
}
public static Result success(a){
Result result = new Result();
result.setCode(CommonConstant.CODE_SUCCESS);
result.setMsg("success");
return result;
}
public static Result success(Object object){
Result result = new Result();
result.setCode(CommonConstant.CODE_SUCCESS);
result.setMsg("success");
result.setData(object);
return result;
}
public static Result success(String msg){
Result result = new Result();
result.setCode(CommonConstant.CODE_SUCCESS);
result.setMsg(msg);
return result;
}
public static Result success(Object object,String successMsg){
Result result = new Result();
result.setCode(CommonConstant.CODE_SUCCESS);
result.setMsg(successMsg);
result.setData(object);
return result;
}
public static Result success(ResponseResultEnum responseResultEnum){
Result result = new Result();
result.setCode(responseResultEnum.getCode());
result.setMsg(responseResultEnum.getMessage());
return result;
}
public static Result success(ResponseResultEnum responseResultEnum,Object data){
Result result = new Result();
result.setCode(responseResultEnum.getCode());
result.setMsg(responseResultEnum.getMessage());
result.setData(data);
return result;
}
public static Result failed(a){
Result result = new Result();
result.setCode(CommonConstant.CODE_BIZ_ERROR);
result.setMsg("fail");
return result;
}
public static Result failed(String errorMsg){
Result result = new Result();
result.setCode(CommonConstant.CODE_BIZ_ERROR);
result.setMsg(errorMsg);
return result;
}
public static Result failed(Object object){
Result result = new Result();
result.setCode(CommonConstant.CODE_BIZ_ERROR);
result.setMsg("fail");
result.setData(object);
return result;
}
public static Result failed(Object object,String errorMsg){
Result result = new Result();
result.setCode(CommonConstant.CODE_BIZ_ERROR);
result.setMsg(errorMsg);
result.setData(object);
return result;
}
public static Result failed(ResponseResultEnum responseResultEnum){
Result result = new Result();
result.setCode(responseResultEnum.getCode());
result.setMsg(responseResultEnum.getMessage());
return result;
}
public static Result failed(ResponseResultEnum responseResultEnum,Object data){
Result result = new Result();
result.setCode(responseResultEnum.getCode());
result.setMsg(responseResultEnum.getMessage());
result.setData(data);
return result;
}
public static Result failed(ResponseResultEnum responseResultEnum,String message){
Result result = new Result();
result.setCode(responseResultEnum.getCode());
result.setMsg(message);
return result;
}
public static Result databaseUpdate(Integer updateResult){
if(updateResult! =null && updateResult>0) return Result.success();
throw new RuntimeException("databaseUpdate failed");
}
public static Result databaseUpdate(Integer updateResult,Object successData){
if(updateResult! =null && updateResult>0) return Result.success(successData);
throw new RuntimeException("databaseUpdate failed");
}
public static boolean isSuccess(BizResult bizResult){
returnbizResult! =null && bizResult.getCode()==0;
}
public static boolean isFailed(BizResult bizResult){
return bizResult==null|| bizResult.getCode()! =0;
}
public static void failedThrow(BizResult bizResult) throws Exception{
if(isFailed(bizResult)) throw new Exception(bizResult.getMsg()==null?"fail":bizResult.getMsg());
}
public static void failedThrow(boolean result) throws Exception{
if(! result)throw new Exception("fail");
}
public static void failedThrowRuntime(BizResult bizResult){
if(isFailed(bizResult)) throw new RuntimeException(bizResult.getMsg()==null?"fail":bizResult.getMsg());
}
public static <T> T nullFailedThrow(T object,String failedMsg){
if(object==null) throw new RuntimeException(failedMsg);
return object;
}
public Result msg(String msg){
this.setMsg(msg);
return this;
}
public Result okMsg(String msg){
if(Result.isSuccess(this)) {this.setMsg(msg);
}
return this;
}
public static BizResult toResult(boolean ok){
returnok? success():failed(); }public static BizResult toResult(boolean ok,String faildMsg){
return ok?success():failed(faildMsg);
}
}
Copy the code
BizResult tools
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
@Setter
@Getter
@Accessors(chain = true)
public class BizResult {
private int code;
private String msg;
private Object data;
public BizResult(a) {}public BizResult(int code, String msg) {
this.code = code;
this.msg = msg;
}
public BizResult(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public boolean ok(a){
return this.getCode()==0;
}
public boolean error(a){
return this.getCode()! =0;
}
public BizResult okMsg(String msg){
if(this.ok()){
this.setMsg(msg);
}
return this; }}Copy the code
7. Common constants utility classes
/** * ${DESCRIPTION} universal Constant **@author x
* @createIn 2020-06-17 he cometh * /
public class CommonConstant {
/************** Interface return status start ****************/
public final static int CODE_SUCCESS = 0;
public final static int CODE_BIZ_ERROR = 1;
public final static int CODE_SYS_ERROR = 2;
public final static int CODE_RPC_ERROR = 3;
public final static int CODE_ZERO_ERROR = 4;
public final static int CODE_CONFIRM = 9;
/************** Interface return status end ****************/
/ * * * * * * * * * * * * * * * * * * * * user token abnormal * * * * * * * * * * * * * * * * * * * * * * * * /
public static final Integer EX_TOKEN_ERROR_CODE = 40101;
public static final Integer EX_USER_INVALID_CODE = 40102;
/ * * * * * * * * * * * * * * * * * * * * the client token abnormal * * * * * * * * * * * * * * * * * * * * * * /
public static final Integer EX_CLIENT_INVALID_CODE = 40131;
public static final Integer EX_CLIENT_FORBIDDEN_CODE = 40331;
public static final Integer EX_OTHER_CODE = 500;
}
Copy the code
8. Exception enumeration classes
public enum ResponseResultEnum {
RESPONSE_RESULT_SUCCEED(0."success"),
RESPONSE_RESULT_FAILURE(1."failure"),
EX_TOKEN_ERROR_CODE(40101."Invalid Token");
ResponseResultEnum(Integer code, String message) {
this.code=code;
this.message=message;
}
private Integer code;
private String message;
public Integer getCode(a){
return this.code;
}
public String getMessage(a){
return this.message; }}Copy the code
9. Token control layer for test verification
import com.alibaba.fastjson.JSONObject;
import com.king.science.config.JwtConfig;
import com.king.science.dto.Result;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@RestController
public class TokenController {
@Resource
private JwtConfig jwtConfig ;
/** * User login interface *@param userName
* @param passWord
* @return* /
@PostMapping("/login")
public Result login (@RequestParam("userName") String userName,
@RequestParam("passWord") String passWord){
JSONObject json = new JSONObject();
System.out.println("Username:"+userName+"; Password:"+passWord);
Return result.errer (); return result.errer (); return result.errer (); */
// Query userId from database by username and password
If the subject needs to store the userId, the createToken method of JwtConfig can be set to Long
String userId = 5 + "";
String token = jwtConfig.createToken(userId) ;
if(! StringUtils.isEmpty(token)) { json.put("token",token) ;
}
return Result.success(json) ;
}
/** * The interface that requires Token authentication */
@PostMapping("/info")
public Result info (a){
return Result.success("info , Welcome to sys"); }/** * Get userId * based on the token in the request header@param request
* @return* /
@GetMapping("/getUserInfo")
public Result getUserInfo(HttpServletRequest request){
String usernameFromToken = jwtConfig.getUsernameFromToken(request.getHeader("token"));
return Result.success(usernameFromToken) ;
}
/* Why can I access info and other token authentication interfaces with the previous token after the project restarts? Answer: as long as it does not expire, it will always exist, similar to redis */
Copy the code
Tests whether the interception works and whether the token is validated
1. Invoke the first interface and log in to obtain the token 2. Invoke the interface with the correct token 3. Invoke the interface using an incorrect token 4. Query the UserId of the user based on the token That’s the whole process of implementing SpringBoot integration with JWT. Thanks for reading!