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

您的位置:首頁技術(shù)文章
文章詳情頁

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

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

1. 前言

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

2. form 登錄的流程

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

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

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

3. Spring Security 中的登錄

昨天 Spring Security 實(shí)戰(zhàn)干貨:自定義配置類入口WebSecurityConfigurerAdapter 中已經(jīng)講到了我們通常的自定義訪問控制主要是通過 HttpSecurity 來構(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 表單登錄

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

4.1 FormLoginConfigurer

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

loginPage(String loginPage) : 登錄 頁面而并不是接口,對于前后分離模式需要我們進(jìn)行改造 默認(rèn)為 /login。 loginProcessingUrl(String loginProcessingUrl) 實(shí)際表單向后臺提交用戶信息的 Action,再由過濾器UsernamePasswordAuthenticationFilter 攔截處理,該 Action 其實(shí)不會處理任何邏輯。 usernameParameter(String usernameParameter) 用來自定義用戶參數(shù)名,默認(rèn) username 。 passwordParameter(String passwordParameter) 用來自定義用戶密碼名,默認(rèn) password failureUrl(String authenticationFailureUrl) 登錄失敗后會重定向到此路徑, 一般前后分離不會使用它。 failureForwardUrl(String forwardUrl) 登錄失敗會轉(zhuǎn)發(fā)到此, 一般前后分離用到它。 可定義一個 Controller (控制器)來處理返回值,但是要注意 RequestMethod。 defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) 默認(rèn)登陸成功后跳轉(zhuǎn)到此 ,如果 alwaysUse 為 true 只要進(jìn)行認(rèn)證流程而且成功,會一直跳轉(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 表單登錄是否放開

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

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

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

5.1 簡單需求

我們的接口訪問都要通過認(rèn)證,登陸錯誤后返回錯誤信息(json),成功后前臺可以獲取到對應(yīng)數(shù)據(jù)庫用戶信息(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(), '登錄失敗了,老哥'); } /** * 登錄成功后拿到個人信息. * * @return the rest */ @PostMapping('/success') public Rest loginSuccess() { // 登錄成功后用戶的認(rèn)證信息 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) 方法進(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 會返回用戶信息:

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

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

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

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

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

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

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

6.1 登錄方式定義

定義登錄方式枚舉 ``。

public enum LoginTypeEnum { /** * 原始登錄方式. */ FORM, /** * Json 提交. */ JSON, /** * 驗(yàn)證碼. */ 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 實(shí)現(xiàn)登錄前置處理過濾器

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

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)證

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

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

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

7. 總結(jié)

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: Spring
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
av资源亚洲| 亚洲人妖在线| 欧美精品资源| 午夜久久中文| 国产一区二区三区久久久久久久久| 欧美一区二区三区久久精品| 国产午夜久久| 亚洲精品中文字幕乱码| 婷婷久久一区| 伊人久久亚洲美女图片| 精品国产美女a久久9999| 国产精品久久久久蜜臀 | a天堂资源在线| 日韩精品第一区| 欧美91精品| 日韩在线麻豆| 黄色不卡一区| 综合亚洲色图| 欧美激情亚洲| 五月天综合网站| 亚洲天堂免费| 国产成人精品一区二区免费看京| 久久精品动漫| 亚洲专区视频| 国产一区二区三区日韩精品| 久久精品天堂| 久久精品国产成人一区二区三区| 国产在视频一区二区三区吞精| 国产精品久久国产愉拍| 国产色99精品9i| 欧美国产精品| 久久亚洲黄色| 丝袜美腿一区| 蜜桃成人精品| 一区在线免费| 国产欧美91| 91亚洲人成网污www| 亚洲国内欧美| 亚洲乱亚洲高清| 国产精品分类| 久久先锋影音| 五月天激情综合网| 国产高清一区二区| 日韩精品一区二区三区中文| 国产精品2023| 国产精品丝袜xxxxxxx| 久久精品国产久精国产爱| 久久国产精品亚洲77777| 国产一区一一区高清不卡| 男人操女人的视频在线观看欧美| 少妇久久久久| 国产欧美精品| 免费视频一区二区| 久久精品免费一区二区三区 | 国产精品一级在线观看| 亚洲免费成人| 国产精品国码视频| 亚洲欧美日韩视频二区| 麻豆精品在线观看| 免费久久99精品国产自在现线| 国产精品成人国产| 亚洲免费黄色| 91亚洲国产高清| 日产欧产美韩系列久久99| 一区二区三区四区日本视频| 视频一区二区国产| 日韩免费看片| 播放一区二区| 蜜桃av在线播放| 国产精品午夜一区二区三区| 免费人成在线不卡| 欧美aa在线观看| 蜜桃国内精品久久久久软件9| 日韩福利一区| 色爱综合av| 热三久草你在线| 视频福利一区| 视频福利一区| 国精品一区二区三区| 色婷婷精品视频| 999国产精品999久久久久久| 国产在线成人| 亚洲一区日本| 亚洲理论在线| 久久激情av| 成午夜精品一区二区三区软件| 国产欧美大片| 在线中文字幕播放| 欧美一级鲁丝片| 亚洲欧洲另类| 国产免费av国片精品草莓男男| 久久99久久人婷婷精品综合| 久久电影tv| 久久亚洲欧美| 无码日韩精品一区二区免费| 91精品国产自产精品男人的天堂| 国产精品黄色片| 婷婷久久一区| 久久精品 人人爱| 91日韩免费| 男女男精品视频网| 亚洲国产欧美日本视频| 在线综合亚洲| 国产69精品久久| 九九久久电影| 免费亚洲一区| 黄色免费成人| 久久三级中文| 亚洲精品影视| 国产精品s色| 亚洲在线电影| 色婷婷色综合| 五月精品视频| 美女av一区| 亚洲三级网址| 91精品综合| 欧美国产亚洲精品| 蜜臀91精品一区二区三区| 精品中国亚洲| 国产伦理久久久久久妇女| 男女男精品视频网| 午夜视频精品| 国产91一区| 日韩在线二区| 国产精品啊v在线| 国产日韩欧美三级| 日产欧产美韩系列久久99| 日韩专区欧美专区| 夜久久久久久| 在线观看免费一区二区| 成人精品中文字幕| 久久久夜精品| 欧美特黄一区| 免费在线看一区| 日本一区福利在线| 日韩在线麻豆| 欧美亚洲国产日韩| 国产精品色婷婷在线观看| 久久国产乱子精品免费女| 福利视频一区| 九一精品国产| 久久青草久久| 久久婷婷久久| 欧美99久久| 亚洲毛片视频| 国产精品网址| 国产不卡精品| 亚洲高清av| 亚洲日韩中文字幕一区| 国产欧美88| 色婷婷久久久| 快she精品国产999| 热久久久久久| 精品一区二区男人吃奶 | 福利一区二区三区视频在线观看| 97人人精品| 免费黄网站欧美| 精品欠久久久中文字幕加勒比| 亚洲爱爱视频| 日韩国产一二三区| 日韩综合在线| 国产亚洲在线观看| 国产日韩亚洲| 9色国产精品| 国产极品嫩模在线观看91精品| 极品裸体白嫩激情啪啪国产精品| 亚洲深夜影院| 久久中文在线| 在线看片一区| 99精品视频在线| 7777精品| 亚洲人妖在线| 亚洲国产综合在线看不卡| 国产免费久久| 久久福利精品| 精品国产中文字幕第一页| 欧美日韩激情| 国产66精品| 亚洲精品免费观看| 久久久久国产| 欧美黄页在线免费观看| 亚洲精品1区| 亚洲涩涩在线| 国产激情综合| 男人的天堂久久精品| 日韩久久精品网| 国产免费久久| 亚洲精品第一| 最新亚洲一区| 久久青草久久| 麻豆久久久久久久| 日韩高清不卡一区| 亚洲一区日韩| 红桃视频欧美| 91精品福利| 亚洲大全视频| 91亚洲一区| 国产精品一区2区3区| 日韩欧美中文在线观看| 久热综合在线亚洲精品|