02 spring security 自定義用戶認證流程

1. 自定義登陸頁面html

(1)首先在static目錄下面建立login.html前端

       注意: springboot項目默承認以訪問resources/resources, resources/staic, resources/public目錄下面的靜態文件java

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登陸頁面</title>
</head>
<body>
<form action="/auth/login" method="post">
    用戶名:<input type="text" name="username">
    <br/>&emsp;碼:<input type="password" name="password">
    <br/>
    <input type="submit" value="登陸">
</form>
</body>
</html>

 

(2) 在spring securiy 配置類中作以下配置web

  @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                // 指定自定義登陸頁面
                .loginPage("/login.html")
                // 登陸url
                .loginProcessingUrl("/auth/login")
                .and()
                .authorizeRequests()
                // 添加一個url匹配器,若是匹配到login.html,就受權
                .antMatchers("/login.html").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                // 關閉spring security默認的防csrf攻擊
                .csrf().disable();
    }

(3) 測試spring

json

 

(4) 存在的問題緩存

 <1> 做爲能夠複用的登陸模塊,咱們應該提供個性化的登陸頁面,也就是說不能寫死只跳轉到login.html。springboot

    此問題比較好解決,使用可配置的登陸頁面,默認使用login.html便可。restful

 <2> 請求跳轉到login.html登陸頁面,貌似沒有什麼問題,但做爲restful風格的接口,通常響應的都是json數據格式,尤爲是app請求。app

    解決思想: 用戶發起數據請求 --> security判斷是否須要身份認證 -----> 跳轉到一個自定義的controller方法 ------> 在該方法內判斷是不是html發起的請求,若是是,就跳轉到login.html,若是不是,響應一個json格式的數據,說明錯誤信息。

 

自定義Controller

@Slf4j
@RestController
public class LoginController {

    /**
     * 請求緩存
     */
    private RequestCache requestCache = new HttpSessionRequestCache();

    /**
     * 重定向工具類
     */
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    /**
     * 若是配置的登陸頁就使用配置的登陸面,不然使用默認的登陸頁面
     */
//    @Value("${xxxx:defaultLoginPage}")
//    private String standardLoginPage;
    private String standardLoginPage = "/login.html";  // 登陸頁

    /**
     * 用戶身份認證方法
     */
    @GetMapping("/user/auth")
    @ResponseStatus(code = HttpStatus.UNAUTHORIZED)  // 返回狀態
    public ResponseData login(HttpServletRequest request, HttpServletResponse response) throws IOException {
        SavedRequest savedRequest = requestCache.getRequest(request, response);
        if (savedRequest != null) {
            String targetUrl = savedRequest.getRedirectUrl();
            log.info("請求是:" + targetUrl);
            // 若是請求是以html結尾
            if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
                redirectStrategy.sendRedirect(request, response, standardLoginPage);
            }
        }
        return new ResponseData("該請求須要登陸,js拿到個人響應數據後,是否須要跳轉到登陸頁面你本身看着辦吧?");
    }
}

 

spring security給該controller的login方法受權

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                // 先進controller中去
                .loginPage("/user/auth")
                // 指定自定義登陸頁面
                .loginPage("/login.html")
                // 登陸url
                .loginProcessingUrl("/auth/login")
                .and()
                .authorizeRequests()
                // 該controller須要受權
                .antMatchers("/user/auth").permitAll()
                // 添加一個url匹配器,若是匹配到login.html,就受權
                .antMatchers("/login.html").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                // 關閉spring security默認的防csrf攻擊
                .csrf().disable();
    }

這樣子就好了!!!

   

2.  自定義登陸成功處理(返回json)

 (1)實現AuthenticationSuccessHandler.java

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Autowired
    private ObjectMapper objectMapper;
    /**
     * Called when a user has been successfully authenticated.
     * @param request
     * @param response
     * @param authentication
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        log.info("登陸成功!!!");
        // 將登陸成功的信息寫到前端
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.getWriter().write(objectMapper.writeValueAsString(authentication));

    }
}

 

(2)修改security 配置類

    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                // 先進controller中去
                .loginPage("/user/auth")
                // 指定自定義登陸頁面
                .loginPage("/login.html")
                // 登陸url
                .loginProcessingUrl("/auth/login")
                .successHandler(myAuthenticationSuccessHandler)
                .and()
                .authorizeRequests()
                // 該controller須要受權
                .antMatchers("/user/auth").permitAll()
                // 添加一個url匹配器,若是匹配到login.html,就受權
                .antMatchers("/login.html").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                // 關閉spring security默認的防csrf攻擊
                .csrf().disable();
    }

 

(3)測試

 

 說明: authentication對象中包含的信息,會由於登陸方式的不一樣而發生改變

 

3. 自定義登陸失敗處理(返回json)

  實現AuthenticationFailureHandler.java 接口便可,跟登陸成敗處理配置同樣。

 

4. 自定義登陸成功處理邏輯

 以上的登陸成功或失敗的返回的都是json,可是在某些狀況下,就是存在着登陸成功或者失敗進行頁面跳轉(spring security默認的處理方式),那麼這種返回json的方式就不合適了。 因此,咱們應該作得更靈活,作成可配置的。

 對於登陸成功邏輯而言只須要對MyAuthenticationSuccessHandler.java稍作修改就行,代碼以下所示:

/**
 * SavedRequestAwareAuthenticationSuccessHandler spring security 默認的成功處理器
 */
@Slf4j
@Component
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 配置的登陸方式
     */
//    @Value("${xxx:默認方式}")
    private String loginType = "JSON";
    /**
     * Called when a user has been successfully authenticated.
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        log.info("登陸成功!!!");

        // 若是配置的登陸方式是JSON,就返回json數據
        if ("JSON".equals(loginType)) {
            // 將登陸成功的信息寫到前端
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.getWriter().write(objectMapper.writeValueAsString(authentication));
        } else {  // 不然就使用默認的跳轉方式
            super.onAuthenticationSuccess(request,response,authentication);
        }
    }
}

 

5. 自定義登陸失敗處理邏輯

 同登陸成功相似,具體代碼以下:

 

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@Component
public class MySimpleUrlAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 配置的登陸方式
     */
//    @Value("${xxx:默認方式}")
    private String loginType = "JSON";
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        log.info("登陸失敗!!!");

        // 若是配置的登陸方式是JSON,就返回json數據
        if ("JSON".equals(loginType)) {
            // 將登陸成功的信息寫到前端
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.getWriter().write(objectMapper.writeValueAsString(exception));
        } else {  // 不然就使用默認的跳轉方式,跳轉到一個錯誤頁面
            super.onAuthenticationFailure(request,response,exception);
        }
    }
}

 

 @Autowired
    private MySimpleUrlAuthenticationFailureHandler mySimpleUrlAuthenticationFailureHandler;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                // 先進controller中去
                .loginPage("/user/auth")
                // 指定自定義登陸頁面
                .loginPage("/login.html")
                // 登陸url
                .loginProcessingUrl("/auth/login")
                .successHandler(myAuthenticationSuccessHandler)
                .failureHandler(mySimpleUrlAuthenticationFailureHandler)
                .and()
                .authorizeRequests()
                // 該controller須要受權
                .antMatchers("/user/auth").permitAll()
                // 添加一個url匹配器,若是匹配到login.html,就受權
                .antMatchers("/login.html").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                // 關閉spring security默認的防csrf攻擊
                .csrf().disable();
    }
相關文章
相關標籤/搜索