The full name of JWT is JSON Web Token. If you understand it literally, it feels like a token based on JSON format for network transmission.
The process is roughly like this:
- The user uses the username and password to request the server
- The server authenticates the user’s information
- The server sends a token to the user through verification
- The client stores the token and brings the token value with each request
- The server verifies the token value and returns the data
Case
Import dependencies
<!-- JWT --> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.10.3</version> </dependency> <!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-core</artifactId> <version>5.8.15</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.11</version> </dependency>
Write a TokenUtils class
code show as below:
package com.hyq.utils; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.hyq.pojo.User; import com.hyq.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import java.util.Date; @Component public class TokenUtils {<!-- --> private static IUserService staticUserService; @Autowired private IUserService userService; /* * The method modified by @PostConstruct will run when the server loads the Servlet, and will only be executed once by the server. * PostConstruct is executed after the constructor and before the init() method. * */ @PostConstruct private void setUserService() {<!-- --> staticUserService = userService; } /** * Generate Token * @param userId * @param sign * @return */ public static String genToken(String userId, String sign) {<!-- --> // import com.auth0.jwt.JWT; return JWT.create().withAudience(userId) //Save user ID in token as payload .withExpiresAt(DateUtil.offsetHour(new Date(), 2)) //The token expires after 2 hours .sign(Algorithm.HMAC256(sign)); //use sign(password) as the token key } /** * Get current user information * @return */ public static User getCurrentUser() {<!-- --> try {<!-- --> HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String token = request. getHeader("token"); if (StrUtil.isNotBlank(token)) {<!-- --> String userId = JWT.decode(token).getAudience().get(0); return staticUserService.getById(Integer.valueOf(userId)); } } catch (Exception e) {<!-- --> return null; } return null; } }
Write a JwtInterceptor class (used to write interception logic)
package com.hyq.config.Interceptor; import cn.hutool.core.util.StrUtil; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; import com.hyq.common.Constants; import com.hyq.exception.ServiceException; import com.hyq.pojo.User; import com.hyq.service.IUserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component @Slf4j public class JwtInterceptor implements HandlerInterceptor {<!-- --> @Autowired private IUserService userService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {<!-- --> String token = request.getHeader("token"); //Get the token from the http request header // If it is not mapped to a method directly through if (!(handler instanceof HandlerMethod)) {<!-- --> return true; } //Perform authentication if(StrUtil.isBlank(token)) {<!-- --> throw new ServiceException(Constants.CODE_401, "No token, please log in again"); } // Get the data in the token String userId; try {<!-- --> userId = JWT.decode(token).getAudience().get(0); } catch (JWTDecodeException j) {<!-- --> throw new ServiceException(Constants.CODE_401, "token verification failed"); } // Verify that the user exists User user = userService. getById(userId); if(user == null) {<!-- --> throw new ServiceException(Constants.CODE_401, "User does not exist, please log in again"); } // User password verification token JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build(); try{<!-- --> jwtVerifier.verify(token); //verify token } catch (JWTDecodeException e) {<!-- --> throw new ServiceException(Constants.CODE_401, "User does not exist, please log in again"); } return true; } }
Write a WebConfig class (used to intercept requests here except the login interface)
package com.hyq.config; import com.hyq.config.Interceptor.JwtInterceptor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Slf4j @Configuration public class WebConfig implements WebMvcConfigurer {<!-- --> @Autowired JwtInterceptor jwtInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) {<!-- --> registry.addInterceptor(jwtInterceptor) //JwtInterceptor implements HandlerInterceptor .addPathPatterns("/**") .excludePathPatterns("/login"); } }
You can start using it now. First, try to use postman to request other interfaces other than /login to indicate success as follows:
Let’s use a login interface to try to generate a token:
Service layer interface code:
package com.hyq.service; import com.hyq.common.Result; import com.hyq.pojo.User; import com.baomidou.mybatisplus.extension.service.IService; import com.hyq.pojo.dto.LoginDTO; import com.hyq.pojo.request.LoginRequest; /** * <p> * Service class *</p> * * @author hyq * @since 2023-03-24 */ public interface IUserService extends IService<User> {<!-- --> LoginDTO login(LoginRequest request); }
service layer code
package com.hyq.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.hyq.common.Constants; import com.hyq.exception.ServiceException; import com.hyq.mapper.UserMapper; import com.hyq.pojo.User; import com.hyq.pojo.dto.LoginDTO; import com.hyq.pojo.request.LoginRequest; import com.hyq.service.IUserService; import com.hyq.utils.TokenUtils; import org.springframework.stereotype.Service; /** * <p> * Service implementation class *</p> * * @author hyq * @since 2023-03-24 */ @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {<!-- --> @Override public LoginDTO login(LoginRequest request) {<!-- --> User one = getUserInfo(request); if (one != null) {<!-- --> LoginDTO loginDTO = new LoginDTO(); BeanUtil. copyProperties(one, loginDTO, true); //Set token String token = TokenUtils.genToken(one.getId().toString(), one.getPassword()); loginDTO.setToken(token); return loginDTO; } else {<!-- --> throw new ServiceException(Constants.CODE_600, "Username or password error"); } } private User getUserInfo(LoginRequest request){<!-- --> QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("username", request.getUsername()); queryWrapper.eq("password", request.getPassword()); User one; // here cannot be assigned a value of null, after the try will directly throw an exception try {<!-- --> one = getOne(queryWrapper); } catch (Exception e) {<!-- --> throw new ServiceException(Constants.CODE_500, "System Error"); } return one; } }
Controller layer code (a result set is encapsulated here, you can go to my blog post to have a look, click here: springboot uniformly encapsulates the result set)
package com.hyq.controller; import com.hyq.common.Constants; import com.hyq.common.Result; import com.hyq.pojo.dto.LoginDTO; import com.hyq.pojo.request.LoginRequest; import com.hyq.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("login") public class LoginController {<!-- --> @Autowired private IUserService userService; @PostMapping public Result login(@RequestBody LoginRequest request) {<!-- --> LoginDTO loginDTO = userService. login(request); if(loginDTO == null) {<!-- --> return Result.error(Constants.CODE_400,"The username or password is wrong"); } return Result. success(loginDTO); } }
Test (token returned successfully):
Let’s take the token and try the query function (note that the query function has been written in advance, so I won’t demonstrate it here)
After we bring the generated token, we can see the result;