Project 1 online communication Platform-2 Development of communication community registration and login module - 6 Interceptor - display login status information

Posted by scristaldi on Mon, 07 Mar 2022 16:12:49 +0100

Refer to Niuke advanced project tutorial

1. Functional requirements for displaying login information

Functions to be realized:

  • Users should display different header information when accessing the page in two states: not logged in and logged in
    • That is, not logged in status:
      • Visit the home page, with login and registration options, and no personal information status display
    • Login status:
      • Visit the home page, without login and registration options, and display personal information status

Why use interceptors and the nature of interceptors

  • Strategy 1: if it is processed in each request,

    • It will increase the duplication of code, and similar logic will be processed in each request
    • The coupling is also very high, and the user information obtained from other requests should be introduced into each request
  • Strategy 2: use request interceptor to process:

    • That is, before each request, after the request, before template rendering and after template rendering, conduct horizontal interception

    • Deal with relevant services in the interceptor, do not change the original request logic, and reduce the degree of coupling and the amount of repeated code

      • Query user information,
      • Hold user information,
      • And show the user data to the template,
      • Clean up user data
    • The use of interceptors is essentially aspect oriented programming and uses the static agent mode

2. Use example of interceptor

2.1 design interceptor implementation class (agent role)

  • That is, define the entry point of interception

To ioc management

​ @Component

Implement interface and rewrite method

HandlerInterceptor
  • Interceptor processing interface
preHandle
  • Execute before Controller
  • return true; Indicates that the execution will continue after the interception processing, and false will not continue
  • Object handler, the method name in the interception request
postHandle
  • After the Controller, the template view is executed before rendering
afterCompletion
  • In the post-processing of template view rendering, cleaning is generally done

    @Component
    public class AlphaInterceptor implements HandlerInterceptor {
        private Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);
    
        // Before executing Controller
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            logger.debug("preHandle" + handler.toString());
            return true;    // true indicates that execution continues after interception processing
        }
    
        // After the Controller, the template view is executed before rendering
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            logger.debug("postHandle" + handler.toString());
        }
    
        // After general rendering, clean up the template
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            logger.debug("afterHandle" + handler.toString());
        }
    }
    

2.2 configuration - what methods are used to intercept which requests

  • That is, configure the aspect of interception and notification method

To ioc management

​ @Configuration

Implement interface and rewrite method

WebMvcConfigurer
registry. Addinterceptor (interceptor class)
  • Which interceptor class to inject - define facets
  • Or return the registry object to facilitate continuous calls to other functions
excludePathPatterns
  • Configure interception paths - exclude which paths do not need to be intercepted
addPathPatterns
  • Add which url path requests need to be intercepted

  • Do not add. By default, all request paths except the excluded paths are added

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
        @Autowired
        AlphaInterceptor alphaInterceptor;
    
        // Injection settings: which interceptor to inject - which paths to intercept (which paths to exclude and which paths to add)
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(alphaInterceptor)
                    .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
                    .addPathPatterns("/register", "/login"); // Do not add. By default, all request paths except the excluded paths are added
        }
    }
    

Test results:

  • Requests that are not in a specific interception path:

    • If requested by homepage / index, there is no relevant log printing information
  • Requests under specific interception path:

    preHandlepublic java.lang.String com.nowcoder.community.controller.LoginController.getLoginPage() // Method name in intercept request
    postHandlepublic java.lang.String com.nowcoder.community.controller.LoginController.getLoginPage()
    afterHandlepublic java.lang.String com.nowcoder.community.controller.LoginController.getLoginPage()
    
    

3. Login registration module interceptor instance

3.1 design interceptor implementation class

Functional requirements to be realized by interceptor class

  • Each request needs to be verified by the server. The ticket in the cookie
  • The server will query the user information through the ticket and submit the user information to the template page for rendering
  • The template page will be rendered and displayed differently according to whether there is user information - so as to distinguish between login and non login status

Therefore, the interceptor should enhance the functions of the request and help each request realize these functions

1)preHandle

  • The goal is to query the user information through the ticket passed in the request before the controller processes the request
    • 1. Because there are many keys in cookie s, you need to design a tool class to get the specified key
    • 2. Query the valid user information according to the ticket
      • Note that you cannot directly query user information through Ticket class, because you need to judge whether t Ticket is valid or not, and login is also invalid
      • Valid user information can only be queried if the t ticket is valid
    • 3. Temporarily store the user information in the current request thread container, that is, hold the user
      • Note that it cannot be simply stored in a class,
        • Therefore, multiple requests to a server and each thread handles one request, which involves the problem of multithreading concurrency
      • Therefore, thread isolation is required. Saving in a session will automatically isolate threads, but session is not suitable for distributed deployment
      • Therefore, we need to design a tool class hostHodel for thread isolation, and use the ThreadLocal class to realize thread isolation
        • The thread is saved in the thread container. Before the request ends, the thread is alive and holds valid user information
1. Design of cookie tool class for obtaining ticket value
request.getCookies()
  • Pay attention to the boundary conditions: air judgment processing,

    • If it is not empty, traverse the cookie array and find the cookie of the specified key
    • If it is empty, the return value is set to null and handed over to the superior for judgment and processing
    public class CookieUtil {
        public static String getValue(HttpServletRequest request, String name) {
            Cookie[] cookies = request.getCookies();
            if(cookies != null) {
                for (Cookie cookie : cookies) {
                    if (cookie.getName().equals(name)) {
                        return cookie.getValue();
                    }
                }
            }
            return null;
        }
    }
    
2. Query user information according to ticket
  • Add a query service in the service layer, and only the t-ticket class can be queried, and further verify whether the t-ticket is valid

    // Query t ticket class according to user voucher ticket
        public LoginTicket selectByTicket(String ticket) {
            return loginTicketMapper.selectByTicket(ticket);
        }
    
.after(new Date()
  • Verify whether the t ticket is valid, and then query the valid user information

    • t ticket is not empty
    • Status is 0, valid status
    • Valid after current time
    // 2. Query user according to ticket
    if(ticket != null) {
        // Query voucher from database
        LoginTicket loginTicket = userService.selectByTicket(ticket);
        if(loginTicket != null &&
           loginTicket.getStatus() == 0 &&
           loginTicket.getExpired().after(new Date())) {
            // If it is valid, find the user information according to the voucher
            User user = userService.findUserById(loginTicket.getUserId());
            // Hold the user information in this request and keep it in the current thread container of the request until the end of the request
            hostHolder.setUsers(user);
        }
    }
    
3. Design thread isolation tool class to store user
ThreadLocal<>
  • ThreadLocal: thread processing tool class, which provides thread safe set and get methods
set(user)
  • Save user information to each thread

    • Get the current thread first, and then save the value in the current thread
users.get()
  • Get the current thread first, and then get the value from the current thread

users.remove()
  • Clean up the value of the current thread

  • Overall code implementation:

    @Component
    public class HostHolder {
        private ThreadLocal<User> users = new ThreadLocal<>();
    
        // Save user information to each thread
        // Get the current thread first, and then save the value in the current thread
        public void setUsers(User user) {
            users.set(user);
        }
    
        // Get the current thread first, and then get the value from the current thread
        public User getUser() {
            return users.get();
        }
    
        // Similarly, clear the user value of the current thread
        public void clear() {
            users.remove();
        } 
    }
    

2)postHandle

  • The goal is to put the user information in modelAndView after being processed by the Controller and before rendering the template

  • The template page renders the page differently according to the user information held (whether there is any)

    // After the Controller, the template view is executed before rendering
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // Obtain user information before rendering and render
        User user = hostHolder.getUser();
        if(user != null && modelAndView != null) {
            logger.debug("postHandle " + user.toString());
            modelAndView.addObject("loginUser", user);
        }
    }
    

3)afterCompletion

  • After the page is rendered, the rendered page will be rendered and displayed to the browser. After the request is processed, the user information in the thread container will be cleared

    // In the post-processing of template view rendering, cleaning is generally done
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            logger.debug("afterHandle " + handler.toString());
            hostHolder.clear();
        }
    }
    

3.2 interceptor configuration

  • After excluding static resources, the default is the request path under all projects

     	@Autowired
        LoginTicketInterceptor loginTicketInterceptor;
    
        // Injection settings: which interceptor to inject - which paths to intercept (which paths to exclude and which paths to add)
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(loginTicketInterceptor)
                    .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");      
        }
    

3.3 carry user status information in the template page

th:if="${loginUser != null}"

  • Dynamically judge whether to display according to whether there is user information held in the request thread
    • Message: render only if it is not empty
    • Registration and login: render only when it is empty
    • User information: render only when it is not empty
      • Dynamically display the user's Avatar and user name as the information of the user held in the thread

test result

  • Before login

  • After login

  • After logging out

Topics: Java Back-end