The opening

Before reading this article, you need to understand Spring Security, or at the very least, finish my previous series of articles.

Secondly, you need to learn JWT (the full name of JSON Web Token). For JWT, you are recommended to refer to Teacher Ruan Yifeng’s JSON Web Token Introduction tutorial, and you can also refer to other materials by yourself.

The body of the

If we have a project with a separate front and back end, the front end directly accessing the back end interface is bound to involve interface access issues.

The usual solution is:

When a client accesses a back-end interface, it needs to carry a token in the request header. Then the interceptor checks the validity of the token on the interface that requires permission to access, and finally decides whether to permit or block the request.

This article will implement these small requirements based on Spring Security + JWT.

Create a project

I made a copy of the project from the previous article, and then made some changes. This time, there are a lot of changes (added and deleted some classes, added some configurations). Of course, I will try to explain the changes as clearly as possible, so you can choose whether to create a new project or make a copy.

Introduce POM dependencies

Compared to the previous article, a JWT dependency package has been added

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <! --JWT(Json Web Token)--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> The < version > 0.9.0 < / version > < / dependency >Copy the code

Example Modify the YML configuration file of Springboot

If you have read the JWT documentation, you will understand the following configuration. If you still don’t understand, you can continue to read.

JWT: tokenHeader: Authorization # JWT stored request header Secret: mySecret # JWT encryption/decryption key expiration: 604800 # JWT out-of-date time (60*60*24) tokenHead: Bearer # JWT load received in the leadCopy the code

Prepare several common classes

For convenience, take a quick look at the utility class copied from elsewhere.

Together, the three classes can return a utility class in JSON format. The code is placed under the Common package.

  • CommonResult Response utility class that returns JSON data to the client. Such as:

{code:200, message:" login successful ", data: null}

  • The IErrorCode interface defines two methods

  • The ResultCode enumeration class implements the IErrorCode interface and defines the result returned by the response

Encapsulate a JWT utility class

A JwtTokenUtil class, also copied from elsewhere, encapsulates methods such as generating tokens and determining the validity of tokens. Don’t worry about how the method is implemented, just use it.

Defining the login Interface

Define a login method in the UserController class, as shown below.

  • You need to call the loadUserByUsername method of the UserDetailService. If you’ve read my previous articles, you should know that this method is mainly about user identity.

  • If the SECURITY API is invoked, the login succeeds


SecurityContextHolder.getContext().setAuthentication(authentication)`
Copy the code
  • The generated token information is returned to the client. This token is used by the client to invoke other interfaces that require user login.

    /** * user login interface * @param username username * @param password password * @return */ @postmapping ("/login") public Object login(@RequestParam("username") String username, @requestParam ("password") String password) {try {// Verify user information UserDetails UserDetails = userDetailsService.loadUserByUsername(username); / / save the user login state UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken (populated userDetails, null, userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); / / to this step, the security will think you login success / / token generated based on user information String token. = jwtTokenUtil generateToken (populated userDetails); return CommonResult.success(token); } catch (AuthenticationException e) {system.out.println (" login exception: "+ LLDB message ()); Return commonResult. failed(" login failed "); }Copy the code

    }

Define a filter

The filter intercepts the requested URL and verifies the token’s validity.

SecurityContextHolder.getContext().setAuthentication(authentication); 
Copy the code

This code on the way, can be as simple as SecurityContextHolder. GetContext () for Security provides a save logged-in user information of a container.

By calling its setAuthentication and getAuthentication methods, user information can be put into and taken out of the container.

/ * * * JWT login authorization filters * / public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@autowired private UserDetailsService userDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; @Value("${jwt.tokenHeader}") private String tokenHeader; @Value("${jwt.tokenHead}") private String tokenHead; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { // 1. String authHeader = Request.getheader (this.tokenheader); String authHeader = Request.getheader (this.tokenheader); // 2. Check whether it is empty and whether it starts with "Bearer" = null && authHeader.startsWith(this.tokenHead)) { String authToken = authHeader.substring(this.tokenHead.length()); / / must begin with "Bearer" / / 3. Get the user name from the token String username = jwtTokenUtil. GetUserNameFromToken (authToken); JWT token is only a secondary login, the real login depends on authentication is valid if (username! = null && SecurityContextHolder. GetContext () getAuthentication () = = null) {/ / is the following code in order to keep the security state of the user login, You can also see in UserController populated UserDetails populated UserDetails = this. UserDetailsService. LoadUserByUsername (username); if (jwtTokenUtil.validateToken(authToken, userDetails)) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); / / core code SecurityContextHolder. GetContext () setAuthentication (authentication); } } } chain.doFilter(request, response); }}Copy the code

Add JwtFilter to Spring Security

Add the following code to the last line in the Configure method of the WebSecurityConfig configuration class. The other code in this method means that you can refer to the previous blog.

@Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); // There must be, 403 Forbidden /* http.formLogin().loginPage("/loginPage.html")// Customize login page.loginProcessingURL ("/form/login")// Customize login Action. SuccessHandler (successHandler)// Custom login handling class. FailureHandler (failureHandler); */ / Request "/form/login", "/ loginpage.html "allow HTTP.authorizerequests ().antmatchers ("/user/userInfo", "/user/login", "/form/login", "/ loginpage.html ").permitall ().antmatchers ("/hello").hasrole ("superadmin") // Only users of the superadmin role can access it .anyRequest().authenticated(); /* http.ExceptionHandling ().AccessdeniedHandler (accessDeniedHandler)// The user has no access to the handler .authenticationEntryPoint(entryPoint); / / the user is not login processor * / / / here is the new code, add an interceptor HTTP. AddFilterBefore (jwtAuthenticationTokenFilter (), UsernamePasswordAuthenticationFilter.class); }Copy the code

conclusion

So far Sprng Securiy series of actual combat blog updated, I hope to help people in need, but also welcome everyone to give comments and criticism.

I also want to add that this series of hands-on articles does not cover how Spring Security works, but how to use it.

The following code, for example, only explains the logic of this code, but does not explain why it is used in this way. These are the things that will be explained later in the principles article.


    UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
    if (jwtTokenUtil.validateToken(authToken, userDetails)) {
        
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }


Copy the code