Simple implementation of JWT verification in SpringBoot project
When using SpringBoot to provide api, I prefer to use jwt for verification. There are many Spring Security integration jwt on the Internet, and Shiro integration jwt, which feels a bit complicated. Here I share my simple implementation in the project.
Dependent Package
In addition to the basic dependencies of SpringBoot, a package for generating jwt and serialization is required. The package that generates jwt depends a lot, because I use the hutool package in my project, so I only use it.
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.9</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.9</version> </dependency>
jwt user model
Define a Jwt sub field model to store users:
import lombok. Data; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; @Data public class JwtUser { /** * user ID */ private Integer id; /** * username */ private String name; /** * Role */ private String role; /** * Get the current requesting user * @return */ public static JwtUser getCurrentUser() { RequestAttributes requestAttributes = RequestContextHolder. currentRequestAttributes(); HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); return (JwtUser) request. getAttribute("user"); } }
Verification Notes
Define an annotation for request classes and methods
import java.lang.annotation.*; @Inherited @Target({ElementType. TYPE, ElementType. METHOD}) @Retention(RetentionPolicy. RUNTIME) public @interface Authorize { /** * Whether anonymous access is available * @return */ boolean anonymous() default false; /** * Role * @return */ String[] roles() default {}; }
JWT help class
Used to generate jwt and parse JwtUser objects.
import cn.hutool.jwt.JWT; import cn.hutool.jwt.JWTUtil; import cn.hutool.jwt.signers.JWTSigner; import cn.hutool.jwt.signers.JWTSignerUtil; import com.google.gson.Gson; import com.mpyf.xapi.security.JwtUser; import lombok.var; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.UUID; public class JwtTokenUtils { public static final String SECRET = "your_secret"; public static final String ISS = "com.your.cn"; private static final int EXPIRATIONHOURS = 24; //The expiration time is 24 hours //create token public static String createToken(JwtUser user) { return createToken(user, EXPIRATIONHOURS); } public static String createToken(JwtUser user, int hours) { String subJson = new Gson().toJson(user); JWTSigner jwtSigner = JWTSignerUtil.hs512(SECRET.getBytes()); JWT jwt = JWT.create().setSigner(jwtSigner); jwt .setJWTId(UUID.randomUUID().toString().replace("-", "")) .setSubject(subJson) //User information .setIssuer(ISS) //Issuer //.setAudience("Audience") //.setNotBefore(new Date()) .setIssuedAt(new Date()) .setExpiresAt(new Date(System.currentTimeMillis() + hours * 3600 * 1000)); return jwt. sign(); } public static JwtUser getUser(String token) { if (StringHelper.isNullOrEmpty(token)) return null; var jwt = JWTUtil. parseToken(token); JWTSigner jwtSigner = JWTSignerUtil.hs512(SECRET.getBytes()); jwt.setSigner(jwtSigner); if (jwt. validate(10)) { var subJson = jwt.getPayload("sub").toString(); JwtUser user = new Gson().fromJson(subJson, JwtUser.class); return user; } else { return null; } } }
Authentication Interceptor
Define the verification interceptor of jwt, get the token from the request header, parse and verify it.
import com.mpyf.xapi.helper.JwtTokenUtils; import com.mpyf.xapi.helper.StringHelper; 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; import java.util.Arrays; /** * jwt verification interceptor */ @Component public class JwtAuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //Authorization:Bearer + space + token String token = request. getHeader("Authorization"); if (token != null) { token = token. replace("Bearer ", ""); } //Process the jwt of the simulated login if (StringHelper. isNullOrEmpty(token)) { token = request. getParameter("jwt"); } if (StringHelper. isNullOrEmpty(token)) { // Compatible with passing token from request parameters Object jwt = request. getAttribute("jwt"); if (jwt != null) { token = jwt.toString(); } } JwtUser user = JwtTokenUtils. getUser(token); request.setAttribute("user", user); if (handler instanceof HandlerMethod) { HandlerMethod h = (HandlerMethod) handler; Authorize authorize = h. getMethodAnnotation(Authorize. class); if (authorize == null) { authorize = h.getMethod().getDeclaringClass().getAnnotation(Authorize.class); } //If there is no Authorize or can be accessed anonymously, return directly if (authorize != null & amp; & amp; !authorize. anonymous()) { { if (user == null) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); return false; } else if (authorize.roles() != null & amp; & amp; authorize.roles().length > 0 & amp; & amp; Arrays.stream(authorize.roles()).allMatch(s -> !s.equalsIgnoreCase(user.getRole()))) { //No permission response.sendError(HttpServletResponse.SC_FORBIDDEN); return false; } } } } return true; } }
Register interceptor
Register interceptors in WebMvc configuration and support cross-domain requests
import com.mpyf.xapi.security.JwtAuthInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.*; @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired JwtAuthInterceptorjwtAuthInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtAuthInterceptor).addPathPatterns("/api/**"); WebMvcConfigurer.super.addInterceptors(registry); } @Override public void addCorsMappings(CorsRegistry registry) { registry. addMapping("/api/**") .allowedOriginPatterns("*") .allowedMethods("*") .allowedHeaders("*") //.maxAge(3600) .allowCredentials(true); WebMvcConfigurer.super.addCorsMappings(registry); } }
Used in Controller
@RestController @RequestMapping("/api/test") @Authorize(roles = {"admin", "user"}) public class TestController { @GetMapping("admin_and_user") public String admin_and_user(){ return "admin and user roles can access"; } @GetMapping("admin_only") @Authorize(roles = "admin") //Override Controller settings public String admin_only(){ return "Only admin role can access"; } @GetMapping("public_all") @Authorize(anonymous = true) public String public_all(){ return "Anonymous can access"; } }
Isn’t it simpler to use Spring Security and Shiro?