Springboot integrates JWT for authentication and authorization

/**
 * @Author Liu Yaofu
 * @create 2021/7/25 10:24
 */
public interface JwtUserService extends IService<JwtUser> {
    JwtUser login(JwtUser user);
}
/**
 * @Author Liu Yaofu
 * @create 2021/7/25 10:18
 */
@Mapper
@Repository
public interface JwtUserMapper extends BaseMapper<JwtUser>{

    JwtUser login(JwtUser user);
}

1.1. Introduction to JWT

JWT (JSON WEB TOKEN): JSON Web Token, JWT is a lightweight secure cross-platform transport format that defines a compact self-contained way to securely transmit information (JSON format) between different entities. It is a standard for transferring data between two entities in a Web environment. What is actually transmitted is a string. In a broad sense, JWT is a standard name; in a narrow sense, JWT refers to the token string used to pass

1.2. What can JWT do?

1. Authorization

This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services and resources allowed by that token. Single sign-on is a feature where JWT is widely used today because it has little overhead and can be easily used across different domains.

1. Information exchange

JSON Web Token s are a great way to securely transfer information between parties. Because the JWT can be signed (e.g. with a public/private key pair), you can be sure that the sender is who they say they are. Additionally, since the signature is calculated using the header and payload, you can also verify that the content has not been tampered with.

1.3. What is the JWT authentication process?

 

 

1.4. What is the structure of JWT?

 

 

1.5. Using JWT

1. Generate token

/*
@SpringBootTest
*/
class JwtApplicationTests {
    
    //Secret key
    private static final String SIGN = "QWERTYUIOPKJHGFDSA";

    public static final byte[] SECRET = "1234567890qwertyuiopasdfghjklzxcvbnm".getBytes();
    /**
     * create token
     */
    @Test
    void createToken() {
        //token expires after 20 seconds
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,60);

        String token = JWT.create()
                //The first part: header, can not write, default value
                .withClaim("userId", "620421199604074511")//Part 2: payload
                .withClaim("userName", "xiaohei")
                .withExpiresAt(instance.getTime())//token expiration time
                .sign(Algorithm.HMAC256(SIGN));//Part 3: Signature
        System.out.println("Generated token for:"+token);
    }
}

The resulting result is:

2. Parse token

/*
@SpringBootTest
*/
class JwtApplicationTests {
    //Secret key
    private static final String SIGN = "QWERTYUIOPKJHGFDSA";

    public static final byte[] SECRET = "1234567890qwertyuiopasdfghjklzxcvbnm".getBytes();

    /**
     * Parse token
     */
    @Test
    void jxToken(){
        String token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHQiOjE2MjczOTc1MzczNjQsInVzZXJOYW1lIjoi5Lyg5p-T55eF5rWL6K-V6LSm5Y-3IiwidXNlcklkIjoiOGE5MmEyOWI3YWFlOWE0MzAxN2FjMWQ5MmRjNTAwMDUiLCJpYXQiOjE2MjczNzU5MzczNjR9.OrfK6_Z8Vw6WconHqYfsIBdt4qAHjcQzdNXKXgvsEuo";
        //Create an authentication object
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
        DecodedJWT decodedJWT = jwtVerifier.verify(token);
        System.out.println("userId for:"+decodedJWT.getClaim("userId").asString());
        System.out.println("userName for:"+decodedJWT.getClaim("userName").asString());
        System.out.println("expiresAt for:"+decodedJWT.getExpiresAt());
    }
}

The result of parsing is:

1.6, package tool class

/**
 * @Author Liu Yaofu
 * @create 2021/7/24 10:53
 */
public class JWTUtils {
    //signature key
    private static final String  SIGNATURE = "q#W#%T<!G*H?CVX~HEW^&*";
    /**
     * 1,Generate token header.payload.signature
     * @return
     */
    public static String getToken(Map<String,String> payload){
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DATE,1);
        //Create JWT builder
        JWTCreator.Builder builder = JWT.create();
        //Part 2: payload
        payload.forEach((k,v)->{
            builder.withClaim(k,v);
        });
        //Specify token expiration time
        String token = builder.withExpiresAt(instance.getTime())
                //Part 3: Signature
                .sign(Algorithm.HMAC256(SIGNATURE));
        return token;
    }
    /**
     * 2,Verify token illegality\Get token information
     * @param token
     */
    public static DecodedJWT verifier(String token){
       return JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);
    }
}

1.7, JWT integration SpringBoot

1. Introduce dependencies

<!--lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

<!-- druid data source driven -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.20</version>
</dependency>

<!--mybatis-plus-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.3.2</version>
</dependency>

<!--mysql-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<!--swagger-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>

2,application.yml

server:
  port: 8989
spring:
  datasource:
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: '0407'
#Aliases, mapper paths, camel case naming rules
mybatis:
  type-aliases-package: com.lyf.jwt.entity
  mapper-locations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true

3. Create the database table jwt_user

4. Create Entity

/**
 * @Author Liu Yaofu
 * @create 2021/7/25 10:13
 */
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@TableName("jwt_user")
@ApiModel(value="User object", description="user table")
public class JwtUser implements Serializable {
    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "primary key")
    @TableId(value = "id", type = IdType.UUID)
    private String id;

    @ApiModelProperty(value = "username")
    @TableField("name")
    private String name;

    @ApiModelProperty(value = "password")
    @TableField("password")
    private String password;
}

5. Create mapper

/**
 * @Author Liu Yaofu
 * @create 2021/7/25 10:18
 */
@Mapper
@Repository
public interface JwtUserMapper extends BaseMapper<JwtUser>{

    JwtUser login(JwtUser user);
}

6. Create service

/**
 * @Author Liu Yaofu
 * @create 2021/7/25 10:24
 */
public interface JwtUserService extends IService<JwtUser> {
    JwtUser login(JwtUser user);
}

7. Create impl

/**
 * @Author Liu Yaofu
 * @create 2021/7/25 10:28
 */
@Service
@Transactional
public class JwtUserServiceImpl extends ServiceImpl<JwtUserMapper, JwtUser> implements JwtUserService {
    @Autowired
    private JwtUserMapper jwtUserMapper;
    @Override
    public JwtUser login(JwtUser user) {
        JwtUser dbUser = jwtUserMapper.login(user);
        if(dbUser!=null){
            return dbUser;
        }
        throw new RuntimeException("Login failed");
    }
}

8. Create controller, generate token token, authenticate

/**
 * @Author Liu Yaofu
 * @create 2021/7/25 10:36
 */
@Slf4j
@RestController
public class JwtUserController {
    @Autowired
    private JwtUserService jwtUserService;
    @GetMapping("/user/login")
    public Map<String,Object> login(JwtUser user){
        Map<String,Object> map = new HashMap<>();
        try{
            JwtUser dbUser = jwtUserService.login(user);
            //Generate token token
            Map<String,String> payload = new HashMap<>();
            payload.put("id", dbUser.getId());
            payload.put("name",dbUser.getName());
            String token = JWTUtils.getToken(payload);
            map.put("state",true);
            map.put("msg","Authentication succeeded");
            map.put("token",token);
        }catch (Exception e){
            map.put("state",false);
            map.put("msg","Login failed");
        }
        return  map;
    }
}

Return result:

9. Resource authorization

/**
     *Authorize the resource token
     * @param token
     * @return
     */
    @PostMapping("/user/getInfo")
    public Map<String,Object> getInfo(String token){
        Map<String,Object> map = new HashMap<>();
        try{
            DecodedJWT verifier = JWTUtils.verifier(token);
            map.put("id",verifier.getClaim("id").asString());
            map.put("name",verifier.getClaim("name").asString());
            map.put("state",true);
            map.put("msg","request succeeded");
            return map;
        }catch (SignatureVerificationException e){
            e.printStackTrace();
            map.put("msg","invalid signature");
        }catch (TokenExpiredException e){
            e.printStackTrace();
            map.put("msg","token Expired");
        }catch (AlgorithmMismatchException e){
            e.printStackTrace();
            map.put("msg","token Algorithm inconsistency");
        }catch (Exception e){
            e.printStackTrace();
            map.put("msg","invalid signature");
        }
        map.put("state",false);
        return map;
    }

Test Results

1.8. Problems caused by small 9 in the above 1.7

Using the above method, the token data must be passed every time, and each method needs to verify the redundancy of the token code. How to optimize it if it is not flexible enough?

Optimizing with Interceptors

1.9, use interceptor for authorization

1. JWT interceptor

/**
 * 1,JWT interceptor
 * @create 2021/7/25 11:51
 * @Author Liu Yaofu
 */
public class JwtInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map<String,Object> map = new HashMap<>();
        //Get the token in the request header
        String token = request.getHeader("token");
        try{
            //verification token
            DecodedJWT verifier = JWTUtils.verifier(token);
            //release request
            return true;
        }catch (SignatureVerificationException e){
            e.printStackTrace();
            map.put("msg","invalid signature");
        }catch (TokenExpiredException e){
            e.printStackTrace();
            map.put("msg","token Expired");
        }catch (AlgorithmMismatchException e){
            e.printStackTrace();
            map.put("msg","token Algorithm inconsistency");
        }catch (Exception e){
            e.printStackTrace();
            map.put("msg","invalid signature");
        }
        map.put("state",false);
        //Convert map to json response to the front end
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(json);
        return false;
    }
}

2. JWT configuration

/**JWT configuration class
 * 2,intercepted request
 * @Author Liu Yaofu
 * @create 2021/7/25 12:23
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //Add JwtInterceptor interceptor
        registry.addInterceptor(new JwtInterceptor())
                .addPathPatterns("/**")//block all paths
                .excludePathPatterns("/user/**");//Exclude paths, paths that generate token s are not intercepted
    }
}

So far jwt learning, springboot integration jwt is completed

Tags: Java Spring Boot jwt

Posted by chrischen on Thu, 02 Jun 2022 18:56:16 +0530