日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Spring Security自定義登錄原理及實現詳解

瀏覽:20日期:2023-08-15 14:42:46

1. 前言

前面的關于 Spring Security 相關的文章只是一個預熱。為了接下來更好的實戰,如果你錯過了請從 Spring Security 實戰系列 開始。安全訪問的第一步就是認證(Authentication),認證的第一步就是登錄。今天我們要通過對 Spring Security 的自定義,來設計一個可擴展,可伸縮的 form 登錄功能。

2. form 登錄的流程

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

Spring Security自定義登錄原理及實現詳解

只要是 form 登錄基本都能轉化為上面的流程。接下來我們看看 Spring Security 是如何處理的。

3. Spring Security 中的登錄

昨天 Spring Security 實戰干貨:自定義配置類入口WebSecurityConfigurerAdapter 中已經講到了我們通常的自定義訪問控制主要是通過 HttpSecurity 來構建的。默認它提供了三種登錄方式:

formLogin() 普通表單登錄 oauth2Login() 基于 OAuth2.0 認證/授權協議 openidLogin() 基于 OpenID 身份認證規范

以上三種方式統統是 AbstractAuthenticationFilterConfigurer 實現的,

4. HttpSecurity 中的 form 表單登錄

啟用表單登錄通過兩種方式一種是通過 HttpSecurity 的 apply(C configurer) 方法自己構造一個 AbstractAuthenticationFilterConfigurer 的實現,這種是比較高級的玩法。 另一種是我們常見的使用 HttpSecurity 的 formLogin() 方法來自定義 FormLoginConfigurer 。我們先搞一下比較常規的第二種。

4.1 FormLoginConfigurer

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

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

知道了這些我們就能來搞個定制化的登錄了。

5. Spring Security 聚合登錄 實戰

接下來是我們最激動人心的實戰登錄操作。 有疑問的可認真閱讀 Spring 實戰 的一系列預熱文章。

5.1 簡單需求

我們的接口訪問都要通過認證,登陸錯誤后返回錯誤信息(json),成功后前臺可以獲取到對應數據庫用戶信息(json)(實戰中記得脫敏)。

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

@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(), '登錄失敗了,老哥'); } /** * 登錄成功后拿到個人信息. * * @return the rest */ @PostMapping('/success') public Rest loginSuccess() { // 登錄成功后用戶的認證信息 UserDetails會存在 安全上下文寄存器 SecurityContextHolder 中 User principal = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); String username = principal.getUsername(); SysUser sysUser = sysUserService.queryByUsername(username); // 脫敏 sysUser.setEncodePassword('[PROTECT]'); return RestBody.okData(sysUser,'登錄成功'); } }

然后 我們自定義配置覆寫 void configure(HttpSecurity http) 方法進行如下配置(這里需要禁用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 或者其它工具進行 Post 方式的表單提交 http://localhost:8080/process?username=Felordcn&password=12345 會返回用戶信息:

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

把密碼修改為其它值再次請求認證失敗后 :

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

6. 多種登錄方式的簡單實現

就這么完了么?現在登錄的花樣繁多。常規的就有短信、郵箱、掃碼 ,第三方是以后我要講的不在今天范圍之內。 如何應對想法多的產品經理? 我們來搞一個可擴展各種姿勢的登錄方式。我們在上面 2. form 登錄的流程 中的 用戶 和 判定 之間增加一個適配器來適配即可。 我們知道這個所謂的 判定就是 UsernamePasswordAuthenticationFilter 。

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

我突然覺得可以模仿 DelegatingPasswordEncoder 的搞法, 維護一個注冊表執行不同的處理策略。當然我們要實現一個 GenericFilterBean 在 UsernamePasswordAuthenticationFilter 之前執行。同時制定登錄的策略。

6.1 登錄方式定義

定義登錄方式枚舉 ``。

public enum LoginTypeEnum { /** * 原始登錄方式. */ FORM, /** * Json 提交. */ JSON, /** * 驗證碼. */ CAPTCHA }

6.2 定義前置處理器接口

public interface LoginPostProcessor { /** * 獲取 登錄類型 * * @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 實現登錄前置處理過濾器

該過濾器維護了 LoginPostProcessor 映射表。 通過前端來判定登錄方式進行策略上的預處理,最終還是會交給

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; /** * 預登錄控制器 * * @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]; } /** * 默認還是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 驗證

通過 POST 表單提交方式 http://localhost:8080/process?username=Felordcn&password=12345&login_type=0 可以請求成功。或者以下列方式也可以提交成功:

Spring Security自定義登錄原理及實現詳解

更多的登錄方式 只需要實現接口 LoginPostProcessor 注入 PreLoginFilter

7. 總結

今天我們通過各種技術的運用實現了從簡單登錄到可動態擴展的多種方式并存的實戰運用。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持好吧啦網。

標簽: Spring
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
视频在线观看国产精品| 日韩国产专区| 蜜桃视频欧美| 亚洲午夜黄色| 国产视频一区在线观看一区免费| 欧美日韩视频| 日韩一区欧美二区| 日韩和欧美的一区| 久久av中文| 中文字幕一区久| 亚洲女同中文字幕| 亚洲欧美专区| 青青伊人久久| 精品久久精品| 黑丝一区二区| 欧美伊人久久| 日本精品不卡| 视频一区在线播放| 国产精品久久久久久妇女 | 久久精品99国产国产精| 欧美精品不卡| 欧美精品资源| 蜜臀久久久99精品久久久久久| 久久精品99国产精品| 福利一区视频| 午夜国产一区二区| 青青草国产成人99久久| 在线天堂中文资源最新版| 影音国产精品| 国产九九精品| 欧美高清不卡| 国产精品亚洲综合在线观看| 久久久久午夜电影| 国产亚洲精品美女久久| 久久久9色精品国产一区二区三区| 亚洲精品日韩久久| 亚洲三级欧美| 日本一区二区中文字幕| 美女一区网站| 在线综合视频| 国产精品原创| 久久三级毛片| 秋霞影院一区二区三区| 久久国产精品毛片| 精品视频国产| 亚洲精品动态| 美女毛片一区二区三区四区| 国产乱子精品一区二区在线观看| 欧美1区2区3区| 国产精品久久久久久久久久白浆 | 精品美女在线视频| 中文字幕乱码亚洲无线精品一区| 国产 日韩 欧美 综合 一区| 亚洲精品三级| 国产综合婷婷| 久久免费视频66| 婷婷成人av| av一区二区高清| 成人国产综合| 国产日韩欧美一区二区三区 | 亚洲精选91| 激情婷婷亚洲| 国产精品专区免费| 国产精品xxx| 日韩精品亚洲专区在线观看| 蜜臀av免费一区二区三区| 国产在线一区不卡| 国产精品夜夜夜| 日韩国产欧美在线视频| 丝袜美腿一区二区三区| 欧美一区二区性| 日韩电影二区| 福利精品在线| 精品国产黄a∨片高清在线| 天堂av一区| 国产精品三上| 亚洲男女av一区二区| 欧美日韩尤物久久| 成人亚洲一区| 美女精品视频在线| 国产精品啊v在线| 国产福利亚洲| 国产精品a级| 国产精品亚洲四区在线观看| 久久精品 人人爱| 国产精品地址| 久久精品一区二区国产| 乱一区二区av| 日韩成人精品一区二区| 精品国产一区二区三区噜噜噜| 亚洲日韩中文字幕一区| 蜜臀av性久久久久蜜臀aⅴ四虎 | 欧美日韩第一| 欧美午夜精彩| 亚洲国产专区校园欧美| 99精品视频在线| 国产韩日影视精品| 精品一区欧美| 亚洲欧美日韩国产一区二区| 天堂va蜜桃一区二区三区| 亚洲视频电影在线| 国产私拍福利精品视频二区| 国产无遮挡裸体免费久久| 国产精品任我爽爆在线播放| 国产成人精选| 久久视频精品| 男女男精品视频网| 欧美日韩亚洲三区| 国产一区二区三区久久| 亚洲天堂1区| 99国产精品99久久久久久粉嫩| 蜜芽一区二区三区| 日韩国产欧美三级| 鲁大师精品99久久久| 日韩欧美三级| 免费美女久久99| 国产日韩免费| 欧美亚洲色图校园春色| 精品一区二区男人吃奶 | 欧美精品不卡| av高清不卡| 亚洲欧美日韩专区| 日本国产欧美| 在线中文字幕播放| 奶水喷射视频一区| 国产精品中文| 韩国三级一区| 免费看精品久久片| 国产毛片久久久| 欧产日产国产精品视频| 激情综合自拍| 91精品国产一区二区在线观看| 久久天堂影院| 在线亚洲激情| 免费视频一区二区三区在线观看| 日本少妇一区| 男女男精品视频网| 国产不卡精品| 蜜桃av一区二区在线观看| 国产精品啊v在线| 亚洲激情精品| 久久99久久久精品欧美| 不卡在线一区二区| 国产日韩欧美一区在线| 香蕉人人精品| 日韩精品导航| 久久精品国产大片免费观看| 综合国产在线| 综合日韩av| 欧美久久一区二区三区| 久久影院一区| 国产精品www.| 亚洲深深色噜噜狠狠爱网站 | 免播放器亚洲| 精品日韩一区| 中文字幕日韩欧美精品高清在线| 老色鬼精品视频在线观看播放| 激情五月综合| 国产精品一卡| 亚洲一区欧美| 99久久亚洲精品| 国产精品一区二区三区av麻| 国产精品外国| 精品美女久久| 亚洲美女久久| 欧美在线资源| 日本久久黄色| 久久av导航| 日本国产欧美| 午夜在线观看免费一区| 电影亚洲精品噜噜在线观看| 日韩精品一区二区三区中文| 婷婷综合激情| 日韩中文影院| 97人人精品| 精品深夜福利视频| 国产精品一区二区美女视频免费看 | 国产精品一区二区精品视频观看| 国精品一区二区| 中文在线中文资源| 麻豆91精品91久久久的内涵| 日韩不卡一区二区三区| 免费中文字幕日韩欧美| 欧美日韩精品一区二区视频| 精品视频在线观看网站| 欧美一区自拍| 青草av.久久免费一区| 中文字幕av亚洲精品一部二部| 欧美美女一区| 婷婷亚洲五月| 婷婷激情综合| 91精品91| 亚洲男女av一区二区| 天堂√中文最新版在线| 国产aⅴ精品一区二区四区| 欧美激情一区| 国产美女视频一区二区| 91麻豆精品激情在线观看最新 | 在线精品一区| 亚洲精品在线二区|