Today I am mainly building a new project. The original project has been logged in by Shiro JWT. In order to enrich the function of permission control of the new project, Spring Security is used for permission control. It is hoped that the two systems can achieve the purpose of single sign-on through JWT. Due to the small scale of the project and the purpose of rapid development, a unified AUth server was not adopted. However, during the construction, referring to some online tutorials, I found that there are many problems in common online tutorials. Most articles are copy the same article, the problem has been left. So I decided to write this article for your reference. If there is any mistake, I hope you can correct me.

JWT is introduced

What is the JWT

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and independent way to securely transfer information between parties as JSON objects.

JWT structure format

JSON Web Tokens by point (.) The three parts of the partition are:

  1. Header: Header information usually consists of two parts: the type of token, which is JWT, and the signature algorithm being used, such as HMAC SHA256 or RSA.
{
    "alg": "HS256"// Signature or digest algorithm"typ": "JWT"/ / token type}Copy the code
  1. The Playload payload part, which is the main content part of the JWT, is also a JSON object that contains the data to be passed. JWT specifies seven default fields to choose from.
{
    "iss": "token-server", / / issues"exp ": "Mon Nov 13 15:28:41 CST 2017",// Expiration time"sub ": "wangjie"/ / user name"aud": "web-server-1"// Receiver,"nbf": "Mon Nov 13 15:40:12 CST 2017"// The token is unavailable before this time"jat": "Mon Nov 13 15:20:41 CST 2017",// Issue time"jti": "0023",// Token ID identifier"claim": {" auth ":" ROLE_ADMIN "}// Access claim}Copy the code
  1. To get the Signature part, you must have an encoded header, an encoded payload, a secret key, and the Signature algorithm specified in the header.
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)
Copy the code

How does the JSON Web token work?

Whenever a user wants to access a protected route or resource, the user agent should send the JWT using bearer mode, usually in the Authorization header. The title should read as follows:

Authorization: Bearer <token>
Copy the code

In some cases, this can be a stateless authorization mechanism. The server’s protected route checks for valid JWT in the Authorization header and, if present, allows users to access protected resources. If the JWT contains the necessary data, it can reduce the need to query the database for certain operations, although this may not always be the case.

Shiro integrates JWT tutorials

Original address of textbook:Juejin. Cn/post / 684490…

Question 1

Public class JWTUtil {error code /** * Verify token is correct * @param token key * @param secret User password * @returnPublic static Boolean verify(String token, String username, String secret) { try { Algorithm algorithm = Algorithm.HMAC256(secret); JWTVerifier verifier = JWT.require(algorithm) .withClaim("username", username)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception exception) {
            return false; }} After the revision, the code token already contains the Playload information (username), Public static Boolean verify(String token, String username, String secret) { try { Algorithm algorithm = Algorithm.HMAC256(secret); JWTVerifier verifier = JWT.require(algorithm) .build(); DecodedJWT jwt = verifier.verify(token);return true;
        } catch (Exception exception) {
            return false; }}Copy the code

Problem 2 @RestControllerAdvice could not catch Shiro exception

Public class JWTFilter extends BasicHttpAuthenticationFilter {/ * * * will return to json illegal request * / @ Override protected Boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException { HttpServletResponse httpResponse = WebUtils.toHttp(response); httpResponse.setCharacterEncoding("UTF-8");
        httpResponse.setContentType("application/json; charset=utf-8");
        httpResponse.setStatus(HttpServletResponse.SC_OK);
        httpResponse.getWriter().write("{\"code\ :401,\" MSG \":\" Not authorized! \",\"ret\":false}");
        return false; }}Copy the code

Spring Security integrates JWT

Original address of textbook:Juejin. Cn/post / 684490…(PS: Most textbooks are similar to this post)

The problem code

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

  
    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain chain) throws ServletException, IOException {
        String authHeader = request.getHeader(this.tokenHeader);
        if(authHeader ! = null && authHeader.startsWith(tokenHead)) { final String authToken = authHeader.substring(tokenHead.length()); // The part after"Bearer "
            String username = jwtTokenUtil.getUsernameFromToken(authToken);

            logger.info("checking authentication " + username);

            if(username ! = null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); / / the key observation here From your token user name, again through the user name from the database for user's detailed information /. / let's have a look at jwtTokenUtil validateToken () exactly what have you done nowif (jwtTokenUtil.validateToken(authToken, userDetails)) {
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                            userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(
                            request));
                    logger.info("authenticated user " + username + ", setting security context"); SecurityContextHolder.getContext().setAuthentication(authentication); } } } chain.doFilter(request, response); }} @Component public class implements Serializable {** * implements Serializable public Boolean validateToken(String) token, UserDetails userDetails) { JwtUser user = (JwtUser) userDetails; String username = getUsernameFromToken(token); // Check the validity of token. // Select * from user where username = 'token'; The username queried in the data comes from the token. // There is an error in this codereturn (username.equals(user.getUsername()) && !isTokenExpired(token));
    }
}
Copy the code

Some doubt

  1. Com.auth0 /java-jwt 3.3.0 and IO. Jsonwebtoken/JJWT / 0.9.0 ensure that headers, playloads (even parameters in the same order) in the token, but the two packets obtained by the token cannot be authenticated. My current approach is to replace all com.auth0/java-jwt

JWT website introduction