SpringBoot-Shiro-jwt, a front-end and back-end separation project, handles the 401 response code solution

Problem phenomenon:

Most projects now have achieved front-end and back-end separation. When shiro is used as the security framework, if the requested token has expired or the request is not authenticated, a 401 HTTP STATUS will be obtained. At this time, a login authentication pop-up box will pop up on the front end because of a 401 error. The effect is as follows:

 After analysis, the browser pops up the bullet box, mainly because the request header contains:

WWW-Authenticate: BASIC realm="application"

When the client (browser) receives a message similar to "WWW-Authenticate: Basic realm="."", a dialog box will pop up asking the user to enter authentication information. In actual projects, we may not want the browser to help with login authentication, but want to return to our own login page.

problem analysis:

First analyze why the background returns 401 and add WWW-Authenticate: BASIC realm="application" in the response header. These may not be found in the project code at all. The source code of this thing is at:

org.apache.shiro.web.filter.authc.HttpAuthenticationFilter

, the detailed screenshot is as follows:

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        boolean loggedIn = false;
        if (this.isLoginAttempt(request, response)) {
            loggedIn = this.executeLogin(request, response);
        }

        if (!loggedIn) {
            this.sendChallenge(request, response);
        }

        return loggedIn;
    }

 The above method is the logic of access rejection processing,​ isLoginAttempt(), which has appeared before, judges whether it is an attempt to log in through the request header, and if true, executes the login logic; otherwise, sendChallenge

    protected boolean sendChallenge(ServletRequest request, ServletResponse response) {
        log.debug("Authentication required: sending 401 Authentication challenge response.");
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
        httpResponse.setStatus(401);
        String authcHeader = this.getAuthcScheme() + " realm=\"" + this.getApplicationName() + "\"";
        httpResponse.setHeader("WWW-Authenticate", authcHeader);
        return false;
    }

Solution:

In fact, after seeing the source code above, the solution is almost there. That is to put

onAccessDenied

Method rewriting, let him take our own implementation method. This article mainly introduces the solution of JWT. Shiro+jwt usually customizes one

JwtFilter extends BasicHttpAuthenticationFilter

You see, that's the point. Now that it is inherited, it is of course possible to override the method of the parent class. As for how to rewrite, what to write depends on your own needs. This article introduces another method without rewriting the onAccessDenied method.

1. First, we customize a 401 status code processing method in JwtFilter

    /**
     * 将非法请求跳转到 /401
     */
    private void response401(ServletRequest req, ServletResponse resp) {
        try {
            HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
            httpServletResponse.sendRedirect("/401");
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
        }
    }

2. Generally, there is an isAccessAllowed method in JwtFilter, which contains the processing logic for authentication failure. We put the method for processing 401 requests in it. The example is as follows:

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (isLoginAttempt(request, response)) {
            try {
                executeLogin(request, response);
            } catch (Exception e) {
                response401(request, response);
            }
        }
        return true;
    }

3. Then set up an interface dedicated to handling /401, the example is as follows:

    @RequestMapping(path = "/401")
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public Result unauthorized() {
        return ResultUtils.success(401, "未授权", null);
    }

4. Finally configure to forward all unauthenticated requests to our custom 401 interface, the configuration is as follows:

 @Bean("shiroFilter")
    public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();

        // 添加自己的过滤器并且取名为jwt
        Map<String, Filter> filterMap = new HashMap<>();
        filterMap.put("jwt", new JWTFilter());
        factoryBean.setFilters(filterMap);

        factoryBean.setSecurityManager(securityManager);
        factoryBean.setUnauthorizedUrl("/401");

        /*
         * 自定义url规则
         * http://shiro.apache.org/web.html#urls-
         */
        Map<String, String> filterRuleMap = new HashMap<>();
        // 所有请求通过我们自己的JWT Filter
        filterRuleMap.put("/**", "jwt");
        // 访问401和404页面不通过我们的Filter
        filterRuleMap.put("/401", "anon");
        factoryBean.setFilterChainDefinitionMap(filterRuleMap);
        return factoryBean;
    }

Guess you like

Origin blog.csdn.net/h363659487/article/details/131064750