In the previous section, we used a custom authentication token class to support multiple logins, but still session-based. In this section, we need to combine JWT to do the authentication function.
demand
When you log in, return the token. Subsequent interfaces use tokens for access.
- After successful login, return token.
- Verify the token when sending the request.
Solution Analysis
Before, we were all session-based. So I need to close the Session first.
Then, in the login success handler, the token is generated and returned.
When sending a business request, an interceptor is used to verify that the token is valid. If it is valid, set it to SecurityContextHoler.
implementation
Token returned after successful login
-
The introduction of JWT package
JDK 11 removed some of the required classes from the JWT package. Let me reintroduce it here.
<! -- to solve all of the following are Jwt tools in Java. Lang. ClassNotFoundException: javax.mail. XML. Bind. DatatypeConverter - > <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-core</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>javax.activation</groupId> <artifactId>activation</artifactId> <version>1.1.1</version> </dependency> <! - above all to solve the Java. Lang. ClassNotFoundException: javax.mail. XML. Bind. DatatypeConverter - > <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> Copy the code
Newer packages can also be introduced in the following ways, and there are some apis that are different from older versions that need to be noted.
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.2</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <! -- or jjwt-gson if Gson is preferred --> <version>0.11.2</version> <scope>runtime</scope> </dependency> Copy the code
-
Modified the method for handling authentication success
filter.setAuthenticationSuccessHandler((request, response, authentication) -> { response.setContentType("application/json; charset=utf-8"); PrintWriter out = response.getWriter(); MyUserDetails userDetails = (MyUserDetails) authentication.getPrincipal(); userDetails.setPassword(null); String jwt = Jwts.builder() .claim("userId", userDetails.getId()) // User role .setSubject(userDetails.getUsername()) / / theme // Expiration time .setExpiration(new Date(System.currentTimeMillis() + 10 * 60 * 1000)) .signWith(SignatureAlgorithm.HS512, "damai") // Encryption algorithm key .compact(); // userDetails Adds the token field userDetails.setToken(jwt); HashMap<String, Object> result = new HashMap<>(); result.put("code"."000000"); result.put("msg"."Successful landing."); result.put("data",userDetails); String s = new ObjectMapper().writeValueAsString(result); out.write(s); out.flush(); out.close(); }); Copy the code
Send a service request to verify the token
-
Adding filters
@Component @Slf4j public class JwtTokenFilter extends OncePerRequestFilter { @Autowired MyUserDetailServiceImpl myUserDetailService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // Spring Security will throw an exception if the token is not authenticated String token = request.getHeader(JwtUtil.TOKEN_HEADER); if (StringUtils.isBlank(token)) { filterChain.doFilter(request,response); return; } log.info("jwtToken:{}",token); / / token Claims claims = Jwts.parser().setSigningKey(JwtUtil.SECRET).parseClaimsJws(token.replace(JwtUtil.TOKEN_HEAD, "")).getBody(); String username = claims.getSubject(); // Load user information UserDetails userDetails = myUserDetailService.loadUserByUsername(username); MyAuthenticationToken myAuthenticationToken = new MyAuthenticationToken(userDetails, null, userDetails.getAuthorities()); // Set to the Security contextSecurityContextHolder.getContext().setAuthentication(myAuthenticationToken); filterChain.doFilter(request,response); }}Copy the code
-
Modifying the Security Configuration
@Override protected void configure(HttpSecurity http) throws Exception { // Custom exception handling when not logged in http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> { response.setContentType("application/json; charset=utf-8"); PrintWriter out = response.getWriter(); HashMap<String, Object> result = new HashMap<>(); result.put("code"."111112"); result.put("msg",authException.getMessage()); out.write(new ObjectMapper().writeValueAsString(result)); out.flush(); out.close(); }); http.authorizeRequests() .anyRequest() .authenticated() .and() .csrf() .disable(); / / close the session http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); / / add JWT filters, attention must be before UsernamePasswordAuthenticationFilter http.addFilterBefore(jwtTokenFilter,UsernamePasswordAuthenticationFilter.class); http.addFilterAt(myAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class); } Copy the code
landing
-
Login return Token
{ "msg": "Successful landing."."code": "000000"."data": { "id": 1."phone": null."username": "Zhang"And..."token": "eyJhbGciOiJIUzUxMiJ9.eyJ1c2VySWQiOjEsInN1YiI6IuW8oOS4iSIsImV4cCI6MTYwNTQ5OTk0Nn0.ZlPGSpyOLaKSeGSdDhuyuLDASJsya1rP2cVdy- JBVKVIBijC6DmOlNRnQzhvFcofWFWqeyhHpGsio1g6HmBHYw"."authorities": [{"authority": "admin"}].}}Copy the code
-
Direct access to the business interface
The request header does not carry the token.
{ "msg": "Full authentication is required to access this resource"."code": "111112" } Copy the code
-
Carries a token and requests a service interface
{ "_links": { "self": { "href": "http://localhost:3344/actuator"."templated": false }, "health": { "href": "http://localhost:3344/actuator/health"."templated": false }, "health-path": { "href": "http://localhost:3344/actuator/health/{*path}"."templated": true }, "info": { "href": "http://localhost:3344/actuator/info"."templated": false}}}Copy the code