Chat filter + interceptor + JWT

One, foreword

I still remember that LAST time I wrote several articles about how to use JWT wechat Pay, Public account authorization + JWT, small program Authorization + JWT in actual projects.

Then, a friend asked me a question like this: Authorized useJWT issued tokenLater,Login and registrationEtc., the user isDon’t need a tokenIn this case, how to exclude the requested URL?

How to use “filter” or “blocker” to filter login and registration

Has anyone written this before?

In writing this article, I have thought of xiaobian onceSoa operationsLooking back at my own code, I laughed funny.

The Controller layer code is similar to this:

@RestController
public class LoginController {

    @Resource
    private HttpServletRequest request;
    
    @Resource
    private JwtUtil	jwtUtil;

    @apiOperation (value = "delete order interface based on order ID ")
    @PostMapping(value = "/deleteById")
    public RespResult deleteById(String id) {
        String header = request.getHeader("token");// Get the header information. The key in the header is a token
        if (StringUtils.isEmpty(header)) {
            return new RespResult(400."Request header cannot be empty"); // A null header returns insufficient permissions
        }
        if(! header.startsWith("Bearer ")) { // As agreed with the front end, header values are Bearer + blank + JWT tokens
            return new RespResult(400."Token error");// Header does not conform to the convention, return permission is insufficient
        }
        String token = header.substring(7);  // Retrieve the token in the header
        Claims claims = jwtUtil.parseJWT(token); // Decrypt the token using the JWT tool
        if (claims == null) {
            return new RespResult(400."Token verification failed");
        }
        // Call the service layer to delete the order
        / /... Is omitted
        return new RespResult(200."Deleted successfully"); }}Copy the code

Lie CAO! Xiaobian this code can be really “crazy”, now only one interface, when there are dozens of interfaces, write dozens of times? Isn’t the code redundant? Besides, 8 hours a day, 7 hours in the copy and paste, I am afraid it is very sour…

Let’s talk about how to use filters or interceptors to kill redundant code.

Three, there is a filter why not

First of all, we need to use filters. We must first understand the principle of filters.

Filter

The Filter is inThe clientwithThe serverA filter between resource files that passes through before accessing themA series of filters (i.e., the filter chain)Proceed with the requestModify, judgeEtc., put the request that does not comply with the rule inIntercept or modify. Responses can also be filtered, blocked, or modified.As shown in the figure, the request made by the browser is first submitted to #A filter intoLine filtering, if the rules are met, the device is released and submitted to the next filter in the filter chain for filtering. The filter is in the chainThe order is related to the order in which it is configured in YML or config.

As you all know, the most important method in filter is the doFilter () method

In doFilter(), chain-.dofilter () is used to filter requests, and chain-.dofilter is used to filter response.

Let’s take a look at the order in which the filter chain code is executed:The picture is so easyChain. The doFilter () beforeTo filter the requested resources (eg:Verify login permissions and control resource access permissionsAfter chain.dofilter (), give some response to the front end, for example:{“code”: “6000”,”message”: “TOKEN validation failed “}

Let’s use filter to optimize the SAO code of xiaobian:

First, in the YML configuration file, set the URI that does not need to be filtered

filter:
  config:
    excludeUrls: /user/login,/user/register  No filtering is required for login and registration
Copy the code

Next, write a filter: MyFilter

@Slf4j
@Order(1) // The smaller the number, the earlier this filter is executed
@Component
@WebFilter(filterName = "MyFilter", urlPatterns = {"/**"}) // Filter rules -- all
public class MyFilter implements Filter {

    @Resource
    private JwtUtils jwtUtils; // own JWT utility class

    @Value("${filter.config.excludeUrls}")
    private String excludeUrls; // Get the URI that does not need to be filtered in the configuration file

    private List<String> excludes;

    // Response message when token authentication fails
    private static final String VALID_ERROR = "{\" code \ ": \" 6000 \ "and \" message \ ": \" TOKEN authentication failure \ "}";

    @Override
    public void init(FilterConfig filterConfig) {
    	/ / initialization
    	// Remove unfiltered URLS from the configuration file, before and after the string whitespace
        excludes = Splitter.on(",").trimResults().splitToList(this.excludeUrls); 
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        String uri = request.getRequestURI(); // Get the request URI
        String token = request.getHeader("token"); // Get the token in the header
        try {
            if (this.isExcludesUrl(uri)) { // Determine whether the request URI needs to be filtered (method below)
                chain.doFilter(req, resp); // No, go ahead
            } else {
                if(! validateParams(token)) {// Verify the token in the header (method below)
                    response.getWriter().write(VALID_ERROR); // The authentication failed, and a verification failure message is returned
                    return;
                }
                chain.doFilter(request, resp); // The verification is successful}}catch (Exception ex) {
            log.error("Exception error", ex);
            response.getWriter().write(VALID_ERROR); // Throw an exception and return a validation failure message
        } finally {
            response.flushBuffer(); // Outputs buffered information to the page}}private boolean validateParams(String token) {
    	// Verify that it is null and that the format meets predefined requirements
        if(! StringUtils.isEmpty(token) && token.startsWith("bearer ")) {
            String token = token.substring(7);
            Map<String, Object> map = jwtUtils.extractJwt(token); // Use the JWT decryption method
            if(map ! =null) { 
                return true;  // Decryption succeeded, return true}}return false; // Decryption failed, return false
    }
    
    private boolean isExcludesUrl(String path) {
        for (String v : this.excludes) { 
            if (path.startsWith(v)) {// Check whether the request URI meets the uri requirements of the configuration file
                return true;  // If the request URI is login, register, return true}}return false; // If the request URI is not login or registration, return false}}Copy the code

Finally, we use Postman to empty request header access interface “” according to the order id delete order interface: localhost: 8080 / deleteById; The result is:

{
    "code": 6000."message": "TOKEN verification failed"
}
Copy the code

In this way, a simple filter replaces the above “hot eye” operation and also excludes interfaces that do not require validation (login, registration, etc.). This is a real SAO…

Four, there are interceptors why not use

Come on, let’s have a lookPrinciple of interceptor; Interceptor, used in ASPect-oriented Programming in AOPA method or fieldIntercept before being accessed and then add some action before or after.Interception is an implementation strategy of AOP.

He has three methods:

Pre-processing, post-processing (Service is called and ModelAndView is returned, but page rendering is not carried out), and return processing (the page has been rendered) are implemented respectively.

  • In preHandle, coding, security controls, and so on can be done before operation;
  • In postHandle, there is an opportunity to modify the ModelAndView to be processed after the operation;
  • AfterCompletion, you can determine whether an exception occurs and log it based on whether ex is null.

Let’s use the interceptor again and optimize the SAO code:

First, write the interceptor configuration file class:

/** * Create By CodeCow on 2020/7/26. */
public class MyInterceptorConfig extends WebMvcConfigurationSupport {

    @Resource
    private MyInterceptor myInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // Register an interceptor, declaring the interceptor object and the request to intercept
        registry.addInterceptor(myInterceptor)
                .addPathPatterns("/ * *") // All paths are blocked
                .excludePathPatterns("/user/login") // Exclude user login requests
                .excludePathPatterns("/user/register"); // Exclude user registration requests}}Copy the code

Next, write the interceptor implementation class

@Component
public class MyInterceptor implements HandlerInterceptor {

    @Resource
    private JwtUtils jwtUtils;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String headToken = request.getHeader("token"); // Get the token in the header
        // Verify that it is null and that the format meets predefined requirements
        if(! StringUtils.isEmpty(headToken) && headToken.startsWith("bearer ")) {
            String token = headToken.substring(7);
            Map<String, Object> map = jwtUtils.extractJwt(token); // Use the JWT decryption method
            if(map ! =null) {
                request.setAttribute("claims_map", map); // Put the JWT decrypted map into the request field
                // Other operations omitted...
            }
            // Other operations omitted...
        }
        return true; }}Copy the code

Finally, when we need to use token, ** directly obtains “claims_map” ** from the request field.

In this way, a simple Interceptor also replaces the SAO operation.

Five, the afterword.

Filters and interceptors with JWT are simple and practical. How to choose a filter and interceptor in a practical project depends on your personal preference and programming style

The principles of both filters and interceptors are pretty much the same. If you understand their principles, you can get only one filter and one Interceptor.

Painted painted painted u recommend

  • Two years, just know how to implement multithreading, ah

  • Promise me no more if/else validation of request parameters, okay

  • Wechat mini program payment + public account payment (including source code)

  • Wechat official account authorization + access to user information + JWT login (source code included)

  • Wechat mini program authorization + obtaining user’s mobile phone number + JWT login (source included)

★★ whisper BB ★★

  • Chinese traditional culture, first carefully, if useful, and then click “like” or “collect”, give yourself a little time to think
  • If you don’t understand, you can leave a comment or write a private letter to make up, and make up will reply to ^_^ in the first time
  • More humorous, witty articles, all in the “CodeCow· program cow” public account

“Java is like the sea, learn to make a boat, sailing on the sea, know the breadth of Java”