SpringSecurity 表單登陸

本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰html

表單登陸

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/mylogin.html")
                .loginProcessingUrl("/doLogin")
                .defaultSuccessUrl("/index.html")
                .failureHandler(new MyAuthenticationFailureHandler())
                .usernameParameter("uname")
                .passwordParameter("passwd")
                .permitAll()
                .and()
                .logout()
                .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/logout1", "GET"),
                        new AntPathRequestMatcher("/logout2", "POST")))
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .defaultLogoutSuccessHandlerFor((req,resp,auth)->{
                    resp.setContentType("application/json;charset=utf-8");
                    Map<String, Object> result = new HashMap<>();
                    result.put("status", 200);
                    result.put("msg", "使用 logout1 註銷成功!");
                    ObjectMapper om = new ObjectMapper();
                    String s = om.writeValueAsString(result);
                    resp.getWriter().write(s);
                },new AntPathRequestMatcher("/logout1","GET"))
                .defaultLogoutSuccessHandlerFor((req,resp,auth)->{
                    resp.setContentType("application/json;charset=utf-8");
                    Map<String, Object> result = new HashMap<>();
                    result.put("status", 200);
                    result.put("msg", "使用 logout2 註銷成功!");
                    ObjectMapper om = new ObjectMapper();
                    String s = om.writeValueAsString(result);
                    resp.getWriter().write(s);
                },new AntPathRequestMatcher("/logout2","POST"))
                .and()
                .csrf().disable();
    }
}
複製代碼

springSecurity須要自定義配置值 基本都是繼承WebSecurityConfigurerAdapter前端

  1. authorizeRequests表示開啓權限配置,.anyRequest().authenticated()表示全部的請求都認證以後才能訪問java

  2. and()方法返回HttpSecurity的實例web

  3. formLogin()表示開啓表單登陸配置spring

    • loginPage 配置登陸頁面地址
    • loginProcessingUrl 配置登陸接口地址
    • defaultSuccessUrl 登陸成功後的跳轉地址
    • failureUrl表示登陸失敗後的跳轉地址
    • usernameParameter表示登陸用戶名的參數名
    • passwordParameter 登陸密碼的參數名
    • permitAll()表示和登陸相關的頁面和接口不作攔截 直接經過

    其中loginProcessingUrl usernameParameter passwordParameter要和登陸表單的配置一致。apache

    .loginPage("/mylogin.html")  // 
                    .loginProcessingUrl("/doLogin")
                    .defaultSuccessUrl("/index.html")
                    .failureHandler(new MyAuthenticationFailureHandler())
                    .usernameParameter("uname")
                    .passwordParameter("passwd")
    複製代碼
  4. csrf().disable()表示禁用CSRF防護功能json

登陸成功

用戶登陸成功後除了defaultSuccessUrl方法能夠實現登陸成功的跳轉以外,successForwardUrl也能夠實現登陸成功後的跳轉,後端

defaultSuccessUrl 和successForwardUrl區別:緩存

  1. defaultSuccessUrl表示當用戶登陸成功後,會自動重定向到登陸以前的地址,若是用戶自己就是訪問的登陸頁面,登陸成功後就會重定向到defaultSuccessUrl指定頁面
  2. successForwardUrl不會考慮用戶以前的訪問地址,登陸成功後經過服務器端跳轉到successForwardUrl所指定的頁面。

defaultSuccessUrl是客戶端跳轉重定向,successForwardUrl是經過服務端實現的跳轉。服務器

他們的接口都AuthenticationSuccessHandler

AuthenticationSuccessHandler有三個實現類

  1. SimpleUrlAuthenticationSuccessHandler 繼承 AbstractAuthenticationTargetUrlRequestHandler 經過他的handle方法處理請求
  2. SavedRequestAwareAuthenticationSuccessHandler 在SimpleUrlAuthenticationSuccessHandler基礎上增長了請求加緩存的功能,能夠記錄以前請求的地址,今兒在登陸成功後重定向到開始訪問的地址。
  3. ForwardAuthenticationSuccessHandler 是服務端的跳轉

SavedRequestAwareAuthenticationSuccessHandler

defaultSuccessUrl 對應的是SavedRequestAwareAuthenticationSuccessHandler

public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
    SavedRequest savedRequest = this.requestCache.getRequest(request, response);
    if (savedRequest == null) {
        super.onAuthenticationSuccess(request, response, authentication);
    } else {
        String targetUrlParameter = this.getTargetUrlParameter();
        if (!this.isAlwaysUseDefaultTargetUrl() && (targetUrlParameter == null || !StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
            this.clearAuthenticationAttributes(request);
            String targetUrl = savedRequest.getRedirectUrl();
            this.logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
            this.getRedirectStrategy().sendRedirect(request, response, targetUrl);
        } else {
            this.requestCache.removeRequest(request, response);
            super.onAuthenticationSuccess(request, response, authentication);
        }
    }
}
複製代碼
  1. 首先從requestCache中獲取緩存下來的請求 若是沒有獲取到緩存請求,就說明用戶在訪問登陸頁面以前並無訪問其餘頁面,此時直接調用父類的onAuthenticationSuccess方法來處理,重定向到defaultSuccessUrl指定的地址。
  2. 獲取targetUrlParameter 拿到target參數後重定向地址。
  3. 若是targetUrlParameter不存在或者alwaysUseDefaultTargetUrl爲true 緩存下來的請求沒有意義,直接調用父類的onAuthenticationSuccess方法完成重定向 。targetUrlParameter存在 則重定向到targetUrlParameter中,alwaysUseDefaultTargetUrl爲true 走默認

ForwardAuthenticationSuccessHandler

successForwardUrl對應ForwardAuthenticationSuccessHandler

public class ForwardAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    private final String forwardUrl;

    public ForwardAuthenticationSuccessHandler(String forwardUrl) {
        Assert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), () -> {
            return "'" + forwardUrl + "' is not a valid forward URL";
        });
        this.forwardUrl = forwardUrl;
    }

    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        request.getRequestDispatcher(this.forwardUrl).forward(request, response);
    }
}
複製代碼

主要調用getRequestDispatcher進行服務端請求轉發

自定義AuthenticationSuccessHandler 實現類

public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler{
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        Map<String, Object> resp = new HashMap<>();
        resp.put("status", 200);
        resp.put("msg", "登陸成功!");
        ObjectMapper om = new ObjectMapper();
        String s = om.writeValueAsString(resp);
        response.getWriter().write(s);
    }
}
複製代碼
.successHandler(new MyAuthenticationSuccessHandler())
複製代碼

經過HttpServletResponse對象返回登陸成功的json給前端

登陸失敗

failureUrl表示登陸失敗後的重定向到配置的頁面,重定向是客戶端的跳轉,不方便攜帶請求失敗的異常信息。

failureForwardUrl是服務端的跳轉,能夠攜帶登陸異常信息。登陸失敗,自動跳轉回登陸頁面,將錯誤信息展現出來。

他們的配置的是AuthenticationFailureHandler接口的實現類

SimpleUrlAuthenticationFailureHandler

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.security.web.authentication;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;

public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler {
    protected final Log logger = LogFactory.getLog(this.getClass());
    private String defaultFailureUrl;
    private boolean forwardToDestination = false;
    private boolean allowSessionCreation = true;
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    public SimpleUrlAuthenticationFailureHandler() {
    }

    public SimpleUrlAuthenticationFailureHandler(String defaultFailureUrl) {
        this.setDefaultFailureUrl(defaultFailureUrl);
    }

    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        if (this.defaultFailureUrl == null) {
            this.logger.debug("No failure URL set, sending 401 Unauthorized error");
            response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
        } else {
            this.saveException(request, exception);
            if (this.forwardToDestination) {
                this.logger.debug("Forwarding to " + this.defaultFailureUrl);
                request.getRequestDispatcher(this.defaultFailureUrl).forward(request, response);
            } else {
                this.logger.debug("Redirecting to " + this.defaultFailureUrl);
                this.redirectStrategy.sendRedirect(request, response, this.defaultFailureUrl);
            }
        }

    }

    protected final void saveException(HttpServletRequest request, AuthenticationException exception) {
        if (this.forwardToDestination) {
            request.setAttribute("SPRING_SECURITY_LAST_EXCEPTION", exception);
        } else {
            HttpSession session = request.getSession(false);
            if (session != null || this.allowSessionCreation) {
                request.getSession().setAttribute("SPRING_SECURITY_LAST_EXCEPTION", exception);
            }
        }

    }
}
複製代碼

當用戶構造SimpleUrlAuthenticationFailureHandler對象時候傳入defaultFailureUrl,也就是登陸失敗時要跳轉的url。在onAuthenticationFailure方法中

  • 若是defaultFailureUrl爲null,直接經過response返回異常信息,不然調用saveException

  • saveException 若是forwardToDestination爲true,表示經過服務器端跳轉回到登陸頁面,此時就把異常信息放到request中。

  • 回到onAuthenticationFailure方法,若是forwardToDestination爲true,就經過服務器端跳回到登陸頁面,不然重定向到登陸頁面。

自定義AuthenticationFailureHandler實現類

public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        Map<String, Object> resp = new HashMap<>();
        resp.put("status", 500);
        resp.put("msg", "登陸失敗!" + exception.getMessage());
        ObjectMapper om = new ObjectMapper();
        String s = om.writeValueAsString(resp);
        response.getWriter().write(s);
    }
}
複製代碼

經過HttpServletResponse對象返回登陸失敗的json給前端

註銷登陸

.logout()
.logoutUrl("")
.logoutRequestMatcher(new OrRequestMatcher(
        new AntPathRequestMatcher("/logout1", "GET"),
        new AntPathRequestMatcher("/logout2", "POST")))
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("")
複製代碼
  • logout() 表示開啓註銷登陸配置。
  • logoutUrl 指定註銷登陸請求地址,默認GET請求,路徑logout
  • invalidateHttpSession 表示是否使session失效,默認爲true
  • clearAuthentication 表示是否清除認證信息,默認爲true
  • logoutSuccessUrl 表示註銷登陸後的跳轉地址。
  • logoutRequestMatcher 匹配多個註銷登陸

自定義註銷成功的返回內容

.logout()
.logoutRequestMatcher(new OrRequestMatcher(
        new AntPathRequestMatcher("/logout1", "GET"),
        new AntPathRequestMatcher("/logout2", "POST")))
.invalidateHttpSession(true)
.clearAuthentication(true)
.defaultLogoutSuccessHandlerFor((req,resp,auth)->{
    resp.setContentType("application/json;charset=utf-8");
    Map<String, Object> result = new HashMap<>();
    result.put("status", 200);
    result.put("msg", "使用 logout1 註銷成功!");
    ObjectMapper om = new ObjectMapper();
    String s = om.writeValueAsString(result);
    resp.getWriter().write(s);
},new AntPathRequestMatcher("/logout1","GET"))
.defaultLogoutSuccessHandlerFor((req,resp,auth)->{
    resp.setContentType("application/json;charset=utf-8");
    Map<String, Object> result = new HashMap<>();
    result.put("status", 200);
    result.put("msg", "使用 logout2 註銷成功!");
    ObjectMapper om = new ObjectMapper();
    String s = om.writeValueAsString(result);
    resp.getWriter().write(s);
},new AntPathRequestMatcher("/logout2","POST"))
.and()
.csrf().disable();
複製代碼

defaultLogoutSuccessHandlerFor()兩個參數 第一個是註銷成功的回調,第二個是具體的註銷請求。

相關文章
相關標籤/搜索