Make writing a habit together! This is the 10th day of my participation in the “Gold Digging Day New Plan · April More text Challenge”. Click here for more details

preface

With the rapid development of applets and H5 applications, the separation of front and back ends has become a trend. However, system authentication is an important part of the system. This article will explain how JWT implements front and back end authentication.

JWT profile

JWT(full name: Json Web Token) is an open standard (RFC 7519) that defines a compact, self-contained way to securely transfer information between parties as Json objects.

Why JWT

2.1 What are the disadvantages of traditional Session authentication?

  • The login information of each user is saved in the Session of the server. As the number of users increases, the overhead of the server increases significantly.

  • Session information stored in the server’s memory will be invalid for distributed applications. Although Session information can be stored in the Redis cache uniformly, this may increase complexity.

  • Because Session authentication is based on cookies, it is not applicable to non-browser terminals and mobile terminals.

  • The system is separated from the front and back ends. Because the front and back ends are cross-domain and Cookie information cannot be crossed, Session authentication cannot be continued rather than cross-domain authentication.

2.2 Advantages of JWT certification

  • Simplicity: JWT Token has a small amount of data and a fast transfer speed.

  • Cross-language: JWT tokens are stored on the client side in JSON-encrypted form, so JWT is cross-language and any Web form is supported. Cross-platform: does not rely on cookies and sessions, and does not need to store session information on the server, which is very suitable for distributed applications and extensions.

JWT data structure

Header

The first JWT part is the header part, which is a Json object that describes the JWT metadata, usually as follows.

{
    "alg": "HS256",
    "typ": "JWT"
}
Copy the code

The alG attribute indicates the algorithm used for the signature. The default value is HMAC SHA256 (written as HS256). The TYP attribute indicates the token type.

Payload

The second part of the JWT is Payload, which is also a Json object. In addition to the data that needs to be passed, there are seven default fields to choose from. Iss: issuer exp: expiration time sub: subject aud: user NBF: unavailable before this time IAT: release time JTI: JWT ID Identifies the JWT

{/ / the default field "sub", "123" theme, / / custom fields "name" : "Java" and "isAdmin" : "true", "loginTime" : "the 2021-12-05 12:00:03"}Copy the code

Note that JWT is unencrypted by default, so anyone can read its contents, so don’t store sensitive information there in case it gets leaked. JSON objects are also converted to strings using the Base64 URL algorithm.

Signature

The signature hash part is used to sign the preceding two parts of data. Header and payload data encoded in Base64 are used to generate the hash using the specified algorithm to ensure that the data is not tampered with.

Spring Boot integrates with JWT

The introduction of Jwt package

<dependency> <groupId> IO. Jsonwebtoken </groupId> <artifactId> JJWT </artifactId> <version>0.9.1</version> </dependency>Copy the code

Write the JWT utility class

Public class JwtUtil {// createJWT public static String createJWT(String subject, String issue, Object claim, Long nowMillis = system.currentTimemillis (); Long expireMillis = nowMillis + ttlMillis; String result = jwts.builder ().setSubject(subject) // Set the subject.setissuer (issue)// issuer.setid (issue)//jwtID .setexpiration (new Date(expireMillis)) // setExpiration Date. Claim ("user", claim)// theme, Can contain user information. SignWith (getSignatureAlgorithm (), getSignedKey ()) / / encryption algorithm. The compressWith (CompressionCodecs. DEFLATE). The compact (); Return result; } JWT public static Jws<Claims> pareseJWT(String JWT) {Jws<Claims> Claims; try { claims = Jwts.parser().setSigningKey(getSignedKey()) .parseClaimsJws(jwt); } catch (Exception ex) { claims = null; } return claims; } public static Claims getClaims(String JWT) {Claims; try { claims = Jwts.parser().setSigningKey(getSignedKey()) .parseClaimsJws(jwt).getBody(); } catch (Exception ex) { claims = null; } return claims; } /** * get Key ** @return Key */ private static Key getSignedKey() {byte[] apiKeySecretBytes = DatatypeConverter .parseBase64Binary(getAuthKey()); Key signingKey = new SecretKeySpec(apiKeySecretBytes, getSignatureAlgorithm().getJcaName()); return signingKey; } private static SignatureAlgorithm getSignatureAlgorithm() { return SignatureAlgorithm.HS256; Public static String getAuthKey() {String auth = "123@#1234"; }Copy the code

Token Authentication interceptor

Component public class TokenInterceptor extends HandlerInterceptorAdapter { public static Log logger = LogManager.getLogger(TokenInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = request.getRequestURI(); logger.info("start TokenInterceptor preHandle.." + uri); / / need to filter the special requests if (SystemUtil isFree (uri) | | SystemUtil. IsProtected (uri) {return true; } String metohd=request.getMethod().toString(); logger.info("TokenInterceptor request method:"+metohd); If (" options ". Equals (metohd)) {return true; } / / whether to open the token authentication Boolean flag = SystemUtil. GetVerifyToken (); ResponseResult result = new ResponseResult(); String Token = request.getHeader(" x-token "); if (flag) { if(StringUtils.isEmpty(token)) { token=request.getParameter("X-Token"); If (stringutils.isempty (token)) {result.setcode (resultcode.need_auth.getCode ());} // Token does not exist if (stringutils.isempty (token)) {result.setcode (resultcode.need_auth.getCode ()); result.setMsg(ResultCode.NEED_AUTH.getMsg()); WebUtil.writeJson(result, response); return false; } else { Claims claims = JwtUtil.getClaims(token); String subject = ""; if (claims ! = null) { subject = claims.getSubject(); If (stringutils.isempty (subject)) {result.setcode (resultcode.invalid_tok.getCode ()); result.setMsg(ResultCode.INVALID_TOKEN.getMsg()); WebUtil.writeJson(result, response); return false; } } else { result.setCode(ResultCode.INVALID_TOKEN.getCode()); result.setMsg(ResultCode.INVALID_TOKEN.getMsg()); WebUtil.writeJson(result, response); return false; } } } return true; }}Copy the code

Configure the blocker

@Configuration
public class WebConfig implements WebMvcConfigurer
{
    @Resource
    private TokenInterceptor tokenInterceptor;

    public void addInterceptors(InterceptorRegistry registry)
    {
        registry.addInterceptor(tokenInterceptor).addPathPatterns("/**");
    }
 }
Copy the code

Login Verification Process

The sample code

@RequestMapping("login") public Result login(HttpServletResponse response) { Map<String, Object> map = new HashMap<>(); // Result result = loginAuth(user); int code = result.getCode(); If (code == resultcode.success) {// The default value is 7 days Long ttlMillis = 7*1000 * 60 * 60 * 24; Long expreTime = system.currentTimemillis () + ttlMillis; String tokenKey = UUID.randomUUID().toString(); String tokenId = JwtUtil.createJWT(user.getUserId(), tokenKey, user.getPassword(), expreTime); map.put("expreTime", expreTime); map.put("tokenId", tokenId); } else { logger.error("login error:" +FastJsonUtil.toJSONString(result)); } return result; }Copy the code

conclusion

The era is progressing, and the technology is constantly updated. In the past, Session+Redis was used to implement single sign-on, but now it can be replaced by JWT +Redis. Only by constantly updating our technology stack can we avoid being eliminated relentlessly.