Before reading this article, you should also have a basic understanding of sessions, cookies, and JWT. In this article, I will not repeat them too much, if the understanding of the three is not clear, you can go here first: after reading this article Session, Cookie, Token, and the interviewer bicker, there is no problem to do the basic understanding and understanding.
If you have a basic understanding of all three, but still have doubts about JWT usage, this article is for you. In this article we will use SpringBoot to integrate JWT to implement a simple token verification, giving us a basic understanding of JWT usage.
SpringBoot integration JWT
First we set up the SpringBoot framework, SpringBoot environment is ready. Do the following:
1. Introduce dependencies
Introducing JWT dependencies, since it is Java-based, requires Java-JWT.
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.5.0</version>
</dependency>
Copy the code
2. Custom annotations
In this step, we define an annotation TokenRequired under the Annotation package that the user needs to be logged in to access other interfaces and so on.
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TokenRequired {
boolean required(a) default true;
}
Copy the code
@target will be the Target of our custom annotation @tokenRequired, because we are targeting the METHOD level for this annotation, so use elementType.method.
@Retention will allow us to customize the @tokenRequired reserved position for annotation @Retention. The reserved location for @tokenrequired is defined as retentionpolicy.runtime annotations of this type are reserved by the JVM and can be read and used at RUNTIME by the JVM or other code that uses reflection.
3. Define entity classes
In the Entity package, we use Lombok to simply customize an entity class, User.
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
String Id;
String username;
String password;
}
Copy the code
4. Define a JWT utility class
In this step, we create a JwtUtil utility class under the Util package that generates and validates tokens.
public class JwtUtil {
// The expiration time is 15 minutes
private static final long EXPIRE_TIME = 15*60*1000;
// The signature will be generated and expire in 15 minutes
public static String sign(String username,String userId,String password){
// Expiration time
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
// Use the user password as the private key for encryption
Algorithm algorithm = Algorithm.HMAC256(password);
// Set the header information
HashMap<String, Object> header = new HashMap<>(2);
header.put("typ"."JWT");
header.put("alg"."HS256");
// Generate signatures with username and userID
return JWT.create().withHeader(header).withClaim("userId",userId)
.withClaim("username",username).withExpiresAt(date).sign(algorithm);
}
/ / validation token
public static boolean verity(String token,String password){
try {
Algorithm algorithm = Algorithm.HMAC256(password);
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(token);
return true;
} catch (IllegalArgumentException e) {
return false;
} catch (JWTVerificationException e) {
return false;
}
}
}
Copy the code
5. Verify services and generate tokens
Under the Service package, we create a UserService and define a login method to validate the business layer data of the login interface and invoke the JwtUtil method to generate tokens.
@Service("UserService")
public class UserService {
@Autowired
UserMapper userMapper;
public String login(String name, String password) {
String token = null;
try {
// Check whether the user exists
User user = userMapper.findByUsername(name);
if(user == null) {
ResultDTO.failure(new ResultError(UserError.EMP_IS_NULL_EXIT));
}else{
// Verify the user password is correct
if(! user.getPassword().equals(password)){
ResultDTO.failure(new ResultError(UserError.PASSWORD_OR_NAME_IS_ERROR));
}else {
// Generate a token and save the user ID and userName into the token
token = JwtUtil.sign(user.getUsername(),user.getId(),user.getPassword());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return token;
}
}
Copy the code
Algorithm.hmac256 (): HS256 is used to generate tokens, and the key is the user’s password. The unique key can be saved on the server.
The information that needs to be stored in the token. Here, I store the user ID in the token.
6. Define interceptors
Next we need to write an interceptor to get the token and validate the token.
public class AuthenticationInterceptor implements HandlerInterceptor {
@Autowired
UserService userService;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
Fetch the token from the HTTP request header
String token = httpServletRequest.getHeader("token");
// If not mapped to the method directly through
if(! (objectinstanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod=(HandlerMethod)object;
Method method=handlerMethod.getMethod();
// Check for annotations that require user permissions
if (method.isAnnotationPresent(TokenRequired.class)) {
TokenRequired userLoginToken = method.getAnnotation(TokenRequired.class);
if (userLoginToken.required()) {
// Perform authentication
if (token == null) {
throw new RuntimeException("No token, please log in again");
}
// Get the user ID in the token
String userId;
try {
userId = JWT.decode(token).getClaim("userId").asString();
} catch (JWTDecodeException j) {
throw new RuntimeException("401");
}
User user = userService.findUserById(userId);
if (user == null) {
throw new RuntimeException("User does not exist, please log in again.");
}
/ / authentication token
try {
if(! JwtUtil.verity(token,user.getPassword())){
throw new RuntimeException("Invalid token");
}
} catch (JWTVerificationException e) {
throw new RuntimeException("401");
}
return true;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
Copy the code
AuthenticationInterceptor interceptor achieved HandlerInterceptor interface of three methods:
Boolean preHandle () :
A preprocessing callback method that preprocesses the processor, takes the third parameter as the responding processor, customizes the Controller return value, and returns true to invoke the next interceptor or processor, or postHandle() and afterCompletion(); False indicates that the process is interrupted and does not continue to call other interceptors or handlers to interrupt execution.
Void postHandle () :
Post-processing callback method to implement post-processing by the processor (called by DispatcherServlet before the view returns to render). At this time, we can process model data or view through modelAndView, modelAndView may also be null.
void afterCompletion():
This callback method is executed only when the corresponding Interceptor’s preHandle() returns true, after the DispatcherServlet has rendered the corresponding view. Used to clear resources.
The execution flow of the interceptor is as follows:
- Retrieve token from HTTP request header;
- Check if there are any annotations that require user permissions, and if so, verify that the token is empty;
- If the token is not empty, query user information and verify the token.
- If the verification succeeds, services are accessed. If the verification fails, invalid token information is displayed.
7. Configure interceptors
The @configuration annotation was added to the Configuration class to indicate that it is a Configuration class and that it will be added to the IOC container as a SpringBean.
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Bean
public AuthenticationInterceptor authenticationInterceptor(a) {
return new AuthenticationInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
/ / will we realized HandlerInterceptor interfaces defined shangbu interceptor instance authenticationInterceptor added InterceptorRegistration, and set up the filtering rules, All requests should pass authenticationInterceptor interception.
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/ * *");
}
}
Copy the code
The WebMvcConfigurer interface is an internal configuration method in Spring that uses Javabeans instead of traditional XML configuration files to implement basic configuration requirements.
AddInterceptor in InterceptorConfig requires an instance of an interceptor that implements the HandlerInterceptor interface. The addPathPatterns method is used to set the interceptor’s filtering path rules.
In addInterceptors approach, we defined step 6 implements the interface HandlerInterceptor authenticationInterceptor interceptor instance, added to the InterceptorRegistration, filtering and set up the path. Now, we are through authenticationInterceptor all requests of interception, the interceptor authenticationInterceptor through preHandle filtering method of business, determine whether have @ TokenRequired to decide whether to need to log in.
8. Define interface methods and add annotations
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
UserService userService;
/ * *
* User login
* @param user
* @return
* /
@PostMapping("/login")
public ResultDTO login( User user){
String token = userService.login(user.getUsername(), user.getPassword());
if (token == null) {
return ResultDTO.failure(new ResultError(UserError.PASSWORD_OR_NAME_IS_ERROR));
}
Map<String, String> tokenMap = new HashMap<>();
tokenMap.put("token", token);
return ResultDTO.success(tokenMap);
}
@TokenRequired
@GetMapping("/hello")
public String getMessage(a){
return "Hello, I'm small.";
}
}
Copy the code
Unannotated words do not validate by default, login interface is generally not validated. So I added a login annotation to getMessage(), stating that the interface must log in to obtain the token, add the token in the request header and pass the authentication before it can be accessed.
Request validation
I added the @tokenRequired annotation to getMessage() in my code, which is now accessible only by logging in to fetch the token value and adding the token in the request header. We now make the following checks:
- Direct access without adding token to request header:
As shown in the figure above, the request result shows: No token, please log in again.
- Access the login interface, obtain the token, and add token information to the request header:In this case, the access succeeds.
- 15 minutes later, the token is invalid. We add token information access to the request header again:At this point the token is invalid, return: invalid token.
conclusion
To review the basic business judgment process used by JWT:
- The user visits the page, the front-end requests the relevant interface, and passes through the interceptor, which extracts the token from the HTTP request header.
- Check whether the interface has the @tokenRequired annotation. If not, pass it. If so, check whether the token is empty;
- If the token is empty, the access fails. If the token is not empty, the user information is queried and the token is verified.
- If the verification succeeds, services are accessed. If the verification fails, invalid token information is displayed.
Disadvantages: This integration is only a simple introduction to JWT usage, and there is no expiration refresh mechanism for tokens. In this case, users need to log in again every 15 minutes. If it is used in the actual production environment, users may be killed, so it is not recommended in actual development.
As for the refreshing mechanism of token, xiao Xiao will interpret and attach the source code for you in the next article.
The integration code address: https://github.com/bailele1995/springboot-jjwt.git
The last
Finally, thank you for reading. The purpose of the article is to record and share, if there are obvious flaws in the article also welcome to point out, we study together in the discussion. Much appreciated!
If you find this article useful, give it a thumbs up. Your encouragement and support keeps me going
Welcome to follow my wechat public account [xiaoxiaozi], we discuss code and life together.
Article Reference:
https://my.oschina.net/odetteisgorgeous/blog/1920762
https://www.zhihu.com/search?type=content&q=spring%20boot%20jwt
https://mp.weixin.qq.com/s/q4upZNTul5Z5WSq2wHC9lg
https://mp.weixin.qq.com/s/Vh-75A7qN8lDo_0DQXCkzg
https://mp.weixin.qq.com/s/KlXc5hWEfgj-Q9cMabeOdA
https://juejin.cn/post/6844904034181070861