Categories
Uncategorized

Spring Security Log separated front and rear ends, illegal requests directly return JSON

hello everybody junior partner, the National Day has finally been done for, Song Ge also come back, starting today we continue to dry hair!

Numerous articles on hair before Spring Security, Song Ge and we talk about this security framework to use:

    Getting your hands with Spring Security!

    Spring Security log in to add a verification code

    SpringSecurity Login using JSON data format

    Spring Security in the role of inheritance

    Spring Security is used JWT!

    Spring Security combined OAuth2

Today, however, and small partners to chat with Spring Security is another problem in that request has not been approved will be redirected to the default login page in the Spring Security, but the back-end login separation of the former, this default behavior then it is very inappropriate, and today we take a look at how to achieve the main not been authenticated requests directly return JSON, instead of redirecting to the login page.

Pre-knowledge

Here on the basic usage of Spring Security I will not repeat them, if little friends do not understand, you can refer to the above six articles.

As we all know, when a custom configuration Spring Security, have several attributes:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .anyRequest().authenticated()
            .formLogin()
            .loginProcessingUrl("/doLogin")
            .loginPage("/login")
            //其他配置
            .permitAll()
            .and()
            .csrf().disable();
}

There are two important attributes:

    loginProcessingUrl: This indicates that the interface address configuration logon request, for example, you are a form login, then the value of the value of the action form the form is filled here.

    loginPage: This indicates the address of the login page, such as when you need to log in after a visit to access the resource, the system will automatically give you this by redirecting Jump to page up.

This configuration login, regardless of the back-end of the front is no problem, log on separate back-end of the front, this configuration have a problem. Let me give a simple example, such as I want to visit / hello interface, but this interface to access later need to sign, I do not log in directly to access this interface, then the system will return to my 302, so I went to the login page, in the first final end, my backend is generally not logged in page, is a prompt JSON, such as this:

@GetMapping("/login")
public RespBean login() {
    return RespBean.error("尚未登录,请登录!");
}

The complete code you can refer to my micro-personnel items.

That is, when I did not go directly to the login access / hello this interface, I see the above paragraph JSON string. Separate back-end development front, this looks good (back-end do not jump page, no matter what happens all return JSON). But the problem lies here, the default jump is a redirect, that when you visit / hello, the server will return to the browser 302, while there is a Location response header field, its value is http : // localhost: 8081 / login, which is to tell your browser to visit http: // localhost: 8081 / login address bar. After the browser receives the instruction, it will directly go to visit http: // localhost: 8081 / login address, if this time is the development environment and the request is an Ajax request, will occur across domains. Because the end of the separation before and after development, we are generally on the front NodeJS start, then the front end of all requests forwarded request made by NodeJS, now request directly to the server address to the browser, and the browser will access directly to http: // localhost : 8081 / login, and forwards the request and will not do, so cross-domain problem occurs.

solution

Obviously, the above problem we can not use cross-domain ideas to solve, although this approach seems also to solve the problem, but not the best option.

If our Spring Security user has not been authenticated in time to request a data request to require authentication, in which case the user is not redirected, but directly returns a JSON, tell the user to initiate this request after authentication is required, it It will not have the above things.

Here involves AuthenticationEntryPoint Spring Security in an interface, the interface has an implementation class: LoginUrlAuthenticationEntryPoint, the class has a method commence can, as follows:

/**
 * Performs the redirect (or forward) to the login form URL.
 */
public void commence(HttpServletRequest request, HttpServletResponse response,
        AuthenticationException authException) {
    String redirectUrl = null;
    if (useForward) {
        if (forceHttps && "http".equals(request.getScheme())) {
            redirectUrl = buildHttpsRedirectUrlForRequest(request);
        }
        if (redirectUrl == null) {
            String loginForm = determineUrlToUseForThisRequest(request, response,
                    authException);
            if (logger.isDebugEnabled()) {
                logger.debug("Server side forward to: " + loginForm);
            }
            RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);
            dispatcher.forward(request, response);
            return;
        }
    }
    else {
        redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);
    }
    redirectStrategy.sendRedirect(request, response, redirectUrl);
}

First, we can see from the comments of this method, this method is used to determine in the end is still to be redirected to forward, through the Debug tracing, we found useForward the default value is false, so the request into the redirection .

So the idea that we solve the problem is simple, direct override this method, the method returns JSON can no longer do redirection, the specific configuration is as follows:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .anyRequest().authenticated()
            .formLogin()
            .loginProcessingUrl("/doLogin")
            .loginPage("/login")
            //其他配置
            .permitAll()
            .and()
            .csrf().disable().exceptionHandling()
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException authException) throws IOException, ServletException {
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                RespBean respBean = RespBean.error("访问失败!");
                if (authException instanceof InsufficientAuthenticationException) {
                    respBean.setMsg("请求失败,请联系管理员!");
                }
                out.write(new ObjectMapper().writeValueAsString(respBean));
                out.flush();
                out.close();
            }
        });
}

In Spring Security configuration AuthenticationEntryPoint plus custom processing method, the method returns directly to the appropriate prompts to JSON. In this way, if the request after a user to go directly access requires authentication before they can access, will not happen redirection, the server directly to the browser a JSON prompt after the browser receives the JSON, the Why Why .

Epilogue

Well, the next question a little redirection and small partners to share, do not know if you have read it? This is my question recently reconstructed micro personnel when encountered. Expected in November, Spring Boot version micro personnel will be upgraded to the latest version, please pay attention to small partners Oh.

关注公众号【江南一点雨】,专注于 Spring Boot+微服务以及前后端分离等全栈技术,定期视频教程分享,关注后回复 Java ,领取松哥为你精心准备的 Java 干货!

Leave a Reply