最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Security自定義登錄原理及實(shí)現(xiàn)詳解

 更新時(shí)間:2020年09月07日 10:48:15   作者:碼農(nóng)小胖哥  
這篇文章主要介紹了Spring Security自定義登錄原理及實(shí)現(xiàn)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

1. 前言

前面的關(guān)于 Spring Security 相關(guān)的文章只是一個(gè)預(yù)熱。為了接下來(lái)更好的實(shí)戰(zhàn),如果你錯(cuò)過(guò)了請(qǐng)從 Spring Security 實(shí)戰(zhàn)系列 開(kāi)始。安全訪問(wèn)的第一步就是認(rèn)證(Authentication),認(rèn)證的第一步就是登錄。今天我們要通過(guò)對(duì) Spring Security 的自定義,來(lái)設(shè)計(jì)一個(gè)可擴(kuò)展,可伸縮的 form 登錄功能。

2. form 登錄的流程

下面是 form 登錄的基本流程:

只要是 form 登錄基本都能轉(zhuǎn)化為上面的流程。接下來(lái)我們看看 Spring Security 是如何處理的。

3. Spring Security 中的登錄

昨天 Spring Security 實(shí)戰(zhàn)干貨:自定義配置類(lèi)入口WebSecurityConfigurerAdapter 中已經(jīng)講到了我們通常的自定義訪問(wèn)控制主要是通過(guò) HttpSecurity 來(lái)構(gòu)建的。默認(rèn)它提供了三種登錄方式:

  • formLogin() 普通表單登錄
  • oauth2Login() 基于 OAuth2.0 認(rèn)證/授權(quán)協(xié)議
  • openidLogin() 基于 OpenID 身份認(rèn)證規(guī)范

以上三種方式統(tǒng)統(tǒng)是 AbstractAuthenticationFilterConfigurer 實(shí)現(xiàn)的,

4. HttpSecurity 中的 form 表單登錄

啟用表單登錄通過(guò)兩種方式一種是通過(guò) HttpSecurity 的 apply(C configurer) 方法自己構(gòu)造一個(gè) AbstractAuthenticationFilterConfigurer 的實(shí)現(xiàn),這種是比較高級(jí)的玩法。 另一種是我們常見(jiàn)的使用 HttpSecurity 的 formLogin() 方法來(lái)自定義 FormLoginConfigurer 。我們先搞一下比較常規(guī)的第二種。

4.1 FormLoginConfigurer

該類(lèi)是 form 表單登錄的配置類(lèi)。它提供了一些我們常用的配置方法:

  • loginPage(String loginPage) : 登錄 頁(yè)面而并不是接口,對(duì)于前后分離模式需要我們進(jìn)行改造 默認(rèn)為 /login。
  • loginProcessingUrl(String loginProcessingUrl) 實(shí)際表單向后臺(tái)提交用戶信息的 Action,再由過(guò)濾器UsernamePasswordAuthenticationFilter 攔截處理,該 Action 其實(shí)不會(huì)處理任何邏輯。
  • usernameParameter(String usernameParameter) 用來(lái)自定義用戶參數(shù)名,默認(rèn) username 。
  • passwordParameter(String passwordParameter) 用來(lái)自定義用戶密碼名,默認(rèn) password
  • failureUrl(String authenticationFailureUrl) 登錄失敗后會(huì)重定向到此路徑, 一般前后分離不會(huì)使用它。
  • failureForwardUrl(String forwardUrl) 登錄失敗會(huì)轉(zhuǎn)發(fā)到此, 一般前后分離用到它。 可定義一個(gè) Controller (控制器)來(lái)處理返回值,但是要注意 RequestMethod。
  • defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) 默認(rèn)登陸成功后跳轉(zhuǎn)到此 ,如果 alwaysUse 為 true 只要進(jìn)行認(rèn)證流程而且成功,會(huì)一直跳轉(zhuǎn)到此。一般推薦默認(rèn)值 false
  • successForwardUrl(String forwardUrl) 效果等同于上面 defaultSuccessUrl 的 alwaysUse 為 true 但是要注意 RequestMethod。
  • successHandler(AuthenticationSuccessHandler successHandler) 自定義認(rèn)證成功處理器,可替代上面所有的 success 方式
  • failureHandler(AuthenticationFailureHandler authenticationFailureHandler) 自定義失敗成功處理器,可替代上面所有的 success 方式
  • permitAll(boolean permitAll) form 表單登錄是否放開(kāi)

知道了這些我們就能來(lái)搞個(gè)定制化的登錄了。

5. Spring Security 聚合登錄 實(shí)戰(zhàn)

接下來(lái)是我們最激動(dòng)人心的實(shí)戰(zhàn)登錄操作。 有疑問(wèn)的可認(rèn)真閱讀 Spring 實(shí)戰(zhàn) 的一系列預(yù)熱文章。

5.1 簡(jiǎn)單需求

我們的接口訪問(wèn)都要通過(guò)認(rèn)證,登陸錯(cuò)誤后返回錯(cuò)誤信息(json),成功后前臺(tái)可以獲取到對(duì)應(yīng)數(shù)據(jù)庫(kù)用戶信息(json)(實(shí)戰(zhàn)中記得脫敏)。

我們定義處理成功失敗的控制器:

 @RestController
 @RequestMapping("/login")
 public class LoginController {
   @Resource
   private SysUserService sysUserService;

   /**
    * 登錄失敗返回 401 以及提示信息.
    *
    * @return the rest
    */
   @PostMapping("/failure")
   public Rest loginFailure() {

     return RestBody.failure(HttpStatus.UNAUTHORIZED.value(), "登錄失敗了,老哥");
   }

   /**
    * 登錄成功后拿到個(gè)人信息.
    *
    * @return the rest
    */
   @PostMapping("/success")
   public Rest loginSuccess() {
      // 登錄成功后用戶的認(rèn)證信息 UserDetails會(huì)存在 安全上下文寄存器 SecurityContextHolder 中
     User principal = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
     String username = principal.getUsername();
     SysUser sysUser = sysUserService.queryByUsername(username);
     // 脫敏
     sysUser.setEncodePassword("[PROTECT]");
     return RestBody.okData(sysUser,"登錄成功");
   }
 }

然后 我們自定義配置覆寫(xiě) void configure(HttpSecurity http) 方法進(jìn)行如下配置(這里需要禁用crsf):

 @Configuration
 @ConditionalOnClass(WebSecurityConfigurerAdapter.class)
 @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
 public class CustomSpringBootWebSecurityConfiguration {

   @Configuration
   @Order(SecurityProperties.BASIC_AUTH_ORDER)
   static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
     @Override
     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       super.configure(auth);
     }

     @Override
     public void configure(WebSecurity web) throws Exception {
       super.configure(web);
     }

     @Override
     protected void configure(HttpSecurity http) throws Exception {
       http.csrf().disable()
           .cors()
           .and()
           .authorizeRequests().anyRequest().authenticated()
           .and()
           .formLogin()
           .loginProcessingUrl("/process")
           .successForwardUrl("/login/success").
           failureForwardUrl("/login/failure");

     }
   }
 }

使用 Postman 或者其它工具進(jìn)行 Post 方式的表單提交 http://localhost:8080/process?username=Felordcn&password=12345 會(huì)返回用戶信息:

 {
   "httpStatus": 200,
   "data": {
     "userId": 1,
     "username": "Felordcn",
     "encodePassword": "[PROTECT]",
     "age": 18
   },
   "msg": "登錄成功",
   "identifier": ""
 }

把密碼修改為其它值再次請(qǐng)求認(rèn)證失敗后 :

  {
    "httpStatus": 401,
    "data": null,
    "msg": "登錄失敗了,老哥",
    "identifier": "-9999"
  }

6. 多種登錄方式的簡(jiǎn)單實(shí)現(xiàn)

就這么完了么?現(xiàn)在登錄的花樣繁多。常規(guī)的就有短信、郵箱、掃碼 ,第三方是以后我要講的不在今天范圍之內(nèi)。 如何應(yīng)對(duì)想法多的產(chǎn)品經(jīng)理? 我們來(lái)搞一個(gè)可擴(kuò)展各種姿勢(shì)的登錄方式。我們?cè)谏厦?2. form 登錄的流程 中的 用戶 和 判定 之間增加一個(gè)適配器來(lái)適配即可。 我們知道這個(gè)所謂的 判定就是 UsernamePasswordAuthenticationFilter 。

我們只需要保證 uri 為上面配置的/process 并且能夠通過(guò) getParameter(String name) 獲取用戶名和密碼即可 。

我突然覺(jué)得可以模仿 DelegatingPasswordEncoder 的搞法, 維護(hù)一個(gè)注冊(cè)表執(zhí)行不同的處理策略。當(dāng)然我們要實(shí)現(xiàn)一個(gè) GenericFilterBean 在 UsernamePasswordAuthenticationFilter 之前執(zhí)行。同時(shí)制定登錄的策略。

6.1 登錄方式定義

定義登錄方式枚舉 ``。

  public enum LoginTypeEnum {

    /**
    * 原始登錄方式.
    */
    FORM,
    /**
    * Json 提交.
    */
    JSON,
    /**
    * 驗(yàn)證碼.
    */
    CAPTCHA
  }

6.2 定義前置處理器接口

  public interface LoginPostProcessor {

    /**
    * 獲取 登錄類(lèi)型
    *
    * @return the type
    */
    LoginTypeEnum getLoginTypeEnum();

    /**
    * 獲取用戶名
    *
    * @param request the request
    * @return the string
    */
    String obtainUsername(ServletRequest request);

    /**
    * 獲取密碼
    *
    * @param request the request
    * @return the string
    */
    String obtainPassword(ServletRequest request);

  }

6.3 實(shí)現(xiàn)登錄前置處理過(guò)濾器

該過(guò)濾器維護(hù)了 LoginPostProcessor 映射表。 通過(guò)前端來(lái)判定登錄方式進(jìn)行策略上的預(yù)處理,最終還是會(huì)交給

 package cn.felord.spring.security.filter;

 import cn.felord.spring.security.enumation.LoginTypeEnum;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
 import org.springframework.web.filter.GenericFilterBean;

 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;

 import static org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY;
 import static org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY;

 /**
  * 預(yù)登錄控制器
  *
  * @author Felordcn
  * @since 16 :21 2019/10/17
  */
 public class PreLoginFilter extends GenericFilterBean {

   private static final String LOGIN_TYPE_KEY = "login_type";

   private RequestMatcher requiresAuthenticationRequestMatcher;
   private Map<LoginTypeEnum, LoginPostProcessor> processors = new HashMap<>();

   public PreLoginFilter(String loginProcessingUrl, Collection<LoginPostProcessor> loginPostProcessors) {
     Assert.notNull(loginProcessingUrl, "loginProcessingUrl must not be null");
     requiresAuthenticationRequestMatcher = new AntPathRequestMatcher(loginProcessingUrl, "POST");
     LoginPostProcessor loginPostProcessor = defaultLoginPostProcessor();
     processors.put(loginPostProcessor.getLoginTypeEnum(), loginPostProcessor);

     if (!CollectionUtils.isEmpty(loginPostProcessors)) {
       loginPostProcessors.forEach(element -> processors.put(element.getLoginTypeEnum(), element));
     }

   }

   private LoginTypeEnum getTypeFromReq(ServletRequest request) {
     String parameter = request.getParameter(LOGIN_TYPE_KEY);

     int i = Integer.parseInt(parameter);
     LoginTypeEnum[] values = LoginTypeEnum.values();
     return values[i];
   }

   /**
    * 默認(rèn)還是Form .
    *
    * @return the login post processor
    */
   private LoginPostProcessor defaultLoginPostProcessor() {
     return new LoginPostProcessor() {

       @Override
       public LoginTypeEnum getLoginTypeEnum() {

         return LoginTypeEnum.FORM;
       }

       @Override
       public String obtainUsername(ServletRequest request) {
         return request.getParameter(SPRING_SECURITY_FORM_USERNAME_KEY);
       }

       @Override
       public String obtainPassword(ServletRequest request) {
         return request.getParameter(SPRING_SECURITY_FORM_PASSWORD_KEY);
       }
     };
   }

   @Override
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
     ParameterRequestWrapper parameterRequestWrapper = new ParameterRequestWrapper((HttpServletRequest) request);
     if (requiresAuthenticationRequestMatcher.matches((HttpServletRequest) request)) {

       LoginTypeEnum typeFromReq = getTypeFromReq(request);

       LoginPostProcessor loginPostProcessor = processors.get(typeFromReq);

       String username = loginPostProcessor.obtainUsername(request);

       String password = loginPostProcessor.obtainPassword(request);

       parameterRequestWrapper.setAttribute(SPRING_SECURITY_FORM_USERNAME_KEY, username);
       parameterRequestWrapper.setAttribute(SPRING_SECURITY_FORM_PASSWORD_KEY, password);

     }

     chain.doFilter(parameterRequestWrapper, response);

   }
 }

6.4 驗(yàn)證

通過(guò) POST 表單提交方式 http://localhost:8080/process?username=Felordcn&password=12345&login_type=0 可以請(qǐng)求成功?;蛘咭韵铝蟹绞揭部梢蕴峤怀晒Γ?/p>

更多的登錄方式 只需要實(shí)現(xiàn)接口 LoginPostProcessor 注入 PreLoginFilter

7. 總結(jié)

今天我們通過(guò)各種技術(shù)的運(yùn)用實(shí)現(xiàn)了從簡(jiǎn)單登錄到可動(dòng)態(tài)擴(kuò)展的多種方式并存的實(shí)戰(zhàn)運(yùn)用。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Springboot使用Maven占位符@替換不生效問(wèn)題及解決

    Springboot使用Maven占位符@替換不生效問(wèn)題及解決

    這篇文章主要介紹了Springboot使用Maven占位符@替換不生效問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • java ssm框架的controller實(shí)現(xiàn)向頁(yè)面?zhèn)鬟f參數(shù)

    java ssm框架的controller實(shí)現(xiàn)向頁(yè)面?zhèn)鬟f參數(shù)

    這篇文章主要介紹了java ssm框架的controller實(shí)現(xiàn)向頁(yè)面?zhèn)鬟f參數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • java編程多線程并發(fā)處理實(shí)例解析

    java編程多線程并發(fā)處理實(shí)例解析

    這篇文章主要介紹了java編程多線程并發(fā)處理實(shí)例解析,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • Springboot 如何設(shè)置啟動(dòng)內(nèi)存

    Springboot 如何設(shè)置啟動(dòng)內(nèi)存

    這篇文章主要介紹了Springboot 如何設(shè)置啟動(dòng)內(nèi)存,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Java中可以實(shí)現(xiàn)負(fù)載均衡的算法詳解

    Java中可以實(shí)現(xiàn)負(fù)載均衡的算法詳解

    這篇文章主要介紹了Java中可以實(shí)現(xiàn)負(fù)載均衡的算法詳解,在Java中,有多種算法可以實(shí)現(xiàn)負(fù)載均衡,下面是兩個(gè)常見(jiàn)的算法示例,隨機(jī)算法和輪詢算法,需要的朋友可以參考下
    2023-08-08
  • 淺析Spring4新特性概述

    淺析Spring4新特性概述

    Spring 4.1并沒(méi)有特別吸引眼球的地方,主要還是增強(qiáng)和一些依賴的版本升級(jí)。本文重點(diǎn)給大家介紹Spring4新特性概述,感興趣的朋友一起看看吧
    2017-09-09
  • 新手初學(xué)Java基礎(chǔ)

    新手初學(xué)Java基礎(chǔ)

    這篇文章主要介紹了java基礎(chǔ)之方法詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-07-07
  • 在Spring?Boot中啟用HTTPS的方法

    在Spring?Boot中啟用HTTPS的方法

    本文介紹了在Spring Boot項(xiàng)目中啟用HTTPS的步驟,從生成SSL證書(shū)開(kāi)始,到配置Spring Boot。HTTPS是保護(hù)Web應(yīng)用程序安全的基石之一,而Spring Boot則提供了相對(duì)簡(jiǎn)易的途徑來(lái)配置它,感興趣的朋友跟隨小編一起看看吧
    2024-02-02
  • java通過(guò)ip獲取客戶端Mac地址的小例子

    java通過(guò)ip獲取客戶端Mac地址的小例子

    java通過(guò)ip獲取客戶端Mac地址的小例子,需要的朋友可以參考一下
    2013-03-03
  • idea創(chuàng)建SpringBoot項(xiàng)目時(shí)Type選maven?project和maven?pom有何區(qū)別

    idea創(chuàng)建SpringBoot項(xiàng)目時(shí)Type選maven?project和maven?pom有何區(qū)別

    Maven是一個(gè)Java工程的管理工具,跟其相同功能的工具如Gradle,下面這篇文章主要給大家介紹了關(guān)于idea創(chuàng)建SpringBoot項(xiàng)目時(shí)Type選maven?project和maven?pom有何區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2023-02-02

最新評(píng)論

奉新县| 剑川县| 鹤壁市| 腾冲县| 盐山县| 颍上县| 双鸭山市| 紫金县| 商都县| 莱西市| 全州县| 孟州市| 新和县| 江达县| 察隅县| 云安县| 湖北省| 怀宁县| 新和县| 柳江县| 平乡县| 临洮县| 永川市| 宁南县| 安徽省| 肃北| 东辽县| 秦皇岛市| 通海县| 杭锦后旗| 增城市| 青冈县| 高州市| 称多县| 黄山市| 大竹县| 达尔| 卢龙县| 登封市| 聂拉木县| 若羌县|