日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring security 学习二

發(fā)布時(shí)間:2024/9/15 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring security 学习二 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

spring security 學(xué)習(xí)二

doc:https://docs.spring.io/spring-security/site/docs/

基于表單的認(rèn)證(個(gè)性化認(rèn)證流程):

一、自定義登錄頁面

1、在securityConfigy配置類中的config方法中添加調(diào)用鏈方法

@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()//指定是表單登錄.loginPage("/cus_login.html")//登錄頁面.and().authorizeRequests()//授權(quán).anyRequest()//任何請(qǐng)求.authenticated();//都需要身份認(rèn)證}

2、同時(shí)在resources/resources下創(chuàng)建一個(gè)html文件

?

<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body>自定義登錄頁面 </body> </html>

/3、配置好之后,啟動(dòng)項(xiàng)目,訪問localhost:9999/h? api

瀏覽器后報(bào)錯(cuò):,重定向到登錄頁面次數(shù)太多

4、為什么出現(xiàn)這種情況:

  因?yàn)樵趕ecurityconfigy配置類中指定了一個(gè)loginPage,但是在方法鏈中,有表明:任何請(qǐng)求都需要認(rèn)證

.anyRequest()//任何請(qǐng)求.authenticated();//都需要身份認(rèn)證

處理:在anyRequest方法前和authorizeRequests方法后添加antMatchers匹配規(guī)則,指定登錄頁面允許訪問

.authorizeRequests()//授權(quán).antMatchers("/cus_login.html").permitAll()

配置完成后再次訪問localhost:9999/h? :,即可跳轉(zhuǎn)至自定義的登錄頁面

5、完善登錄頁面

//在usernamePasswordAuthenticationFilter中處理的action為/login,請(qǐng)求方式是post public UsernamePasswordAuthenticationFilter() {super(new AntPathRequestMatcher("/login", "POST"));} <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> 自定義登錄頁面 <form action="/authentication/form" method="POST"><table><tr><td>用戶名:</td><td><input type="text" name="username"></td></tr><tr><td>密碼:</td><td><input type="text" name="password"></td></tr><tr><td><button type="submit">登錄</button></td></tr></table> </form> </body> </html>

?因?yàn)樵诘卿涰撁嬷性O(shè)定的action為“/authentication/form”,所以在securityConfig配置類的方法鏈中添加loginProcessingUrl方法

@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()//指定是表單登錄.loginPage("/cus_login.html")//登錄頁面.loginProcessingUrl("/authentication/form").and().authorizeRequests()//授權(quán).antMatchers("/cus_login.html").permitAll().anyRequest()//任何請(qǐng)求.authenticated();//都需要身份認(rèn)證}

重啟項(xiàng)目:訪問localhost:9999/h

顯示為自定義的頁面。

csrf().disable();//跨站防護(hù)不適用

6、將loginPage(/cus_login.html) html頁面改為一個(gè)controller代碼

①新建一個(gè)controller

@RestController public class LoginSecurityController {@RequestMapping("/authentication/require")public String requireAuthentication(HttpServletRequest request, HttpServletResponse response) {return null;} }

②修改securityconfig配置類中的config中的方法調(diào)用鏈中的loginPage

@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()//指定是表單登錄.loginPage("/authentication/require")//登錄頁面.loginProcessingUrl("/authentication/form").and().authorizeRequests()//授權(quán).antMatchers("/authentication/require").permitAll().anyRequest()//任何請(qǐng)求.authenticated()//都需要身份認(rèn)證.and().csrf().disable();//跨站防護(hù)不適用}

?

二、自定義登錄成功的處理

?實(shí)現(xiàn)AuthenticationSucessHandler接口,一下實(shí)現(xiàn)方式是:繼承SaveRequestAwareAuthenticationSuccessHandler類(這樣的話是可以根據(jù)請(qǐng)求方式的類型,來返回不同個(gè)數(shù)的數(shù)據(jù),json或者默認(rèn))

package com.nxz.security.handler;import com.fasterxml.jackson.databind.ObjectMapper; import com.nxz.security.core.properties.LoginType; import com.nxz.security.core.properties.SecurityProperties; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.stereotype.Component;import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;@Component("custAuthenticationSuccessHandler") @Slf4j public class CustAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {@Autowired//springmvc自動(dòng)注冊(cè)的一個(gè)mapper類private ObjectMapper objectMapper;@Autowiredprivate SecurityProperties securityProperties;@Overridepublic void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {log.info("登錄成功");if (LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())) {httpServletResponse.setContentType("application/json;UTF-8");httpServletResponse.getWriter().write(objectMapper.writeValueAsString(authentication));} else {super.onAuthenticationSuccess(httpServletRequest, httpServletResponse, authentication);}} } @Autowiredprivate AuthenticationSuccessHandler custAuthenticationSuccessHandler;http.formLogin().loginPage("/authentication/require").loginProcessingUrl("/authentication/form").successHandler(custAuthenticationSuccessHandler).failureHandler(custAuthenticationFailerHandler).and().authorizeRequests().antMatchers("/authentication/require",loginPage,"/code/image").permitAll().anyRequest().authenticated();

?

三、自定義登錄失敗的處理

實(shí)現(xiàn)AuthenticationFailureHandler接口,繼承SimpleUrlAuthenticationFailureHandler,根據(jù)請(qǐng)求方式返回不同的類型

package com.nxz.security.handler;import com.alibaba.fastjson.JSON; import com.fasterxml.jackson.databind.ObjectMapper; import com.nxz.security.core.properties.LoginType; import com.nxz.security.core.properties.SecurityProperties; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; 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("custAuthenticationFailerHandler") public class CustAuthenticationFailerHandler extends SimpleUrlAuthenticationFailureHandler {@Autowiredprivate ObjectMapper objectMapper;@Autowiredprivate SecurityProperties securityProperties;@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {log.info("登錄失敗!");if (LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())) {response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());response.setContentType("application/json;charset=UTF-8");response.getWriter().write(JSON.toJSONString(objectMapper.writeValueAsString(exception)));} else {super.onAuthenticationFailure(request, response, exception);}} } @Autowiredprivate AuthenticationFailureHandler custAuthenticationFailerHandler;

?

?

四、源碼學(xué)習(xí)

1、認(rèn)證流程說明

登錄請(qǐng)求進(jìn)來是,會(huì)先到UsernamePasswordAuthentication類的,其實(shí)最先走的是它的父類AbstractAuthenticationProcessingFilter.java,在父類中會(huì)走attemptAuthentication方法,父類沒有實(shí)現(xiàn),因此會(huì)走子類的方法,當(dāng)認(rèn)證成功后,會(huì)走到最后successFulAuthentication方法

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {。。。。。。。。Authentication authResult;try {authResult = attemptAuthentication(request, response);if (authResult == null) {// return immediately as subclass has indicated that it hasn't completed// authenticationreturn;}sessionStrategy.onAuthentication(authResult, request, response);}。。。。。。。。// Authentication successif (continueChainBeforeSuccessfulAuthentication) {chain.doFilter(request, response);}successfulAuthentication(request, response, chain, authResult);}

attemptAuthentication方法(子類usernamepasswordAuthenticationFilter.java)

public Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException {if (postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}String username = obtainUsername(request);String password = obtainPassword(request);。。。。。。。。username = username.trim();//這個(gè)UsernamepasswordAuthenticationToken其實(shí)就是封裝了username 和password信息UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);// 這個(gè)setDetails會(huì)把請(qǐng)求的一些信息設(shè)置到authRequest對(duì)象中setDetails(request, authRequest);return this.getAuthenticationManager().authenticate(authRequest);}

上邊那個(gè)this.getAuthenticationManager() 會(huì)獲取一個(gè)authenticationManager類

作用:收集相關(guān)的provider,當(dāng)請(qǐng)求到的時(shí)候,循環(huán)判斷是否支持當(dāng)前的provider類型

public interface AuthenticationManager {//authenticate認(rèn)證方法,交給實(shí)現(xiàn)類實(shí)現(xiàn)Authentication authenticate(Authentication var1) throws AuthenticationException; }

他的實(shí)現(xiàn)類(用的就是ProviderManger類),不同的provider支持的Authentication是不同的

?

例如:UsernamePasswordAuthenticationToken類型的Authentication時(shí),provider就是DaoAuthenticationProvider,

?

public Authentication authenticate(Authentication authentication) throws AuthenticationException {Class<? extends Authentication> toTest = authentication.getClass();AuthenticationException lastException = null;AuthenticationException parentException = null;Authentication result = null;Authentication parentResult = null;boolean debug = logger.isDebugEnabled();Iterator var8 = this.getProviders().iterator();while(var8.hasNext()) {AuthenticationProvider provider = (AuthenticationProvider)var8.next();if (provider.supports(toTest)) {if (debug) {logger.debug("Authentication attempt using " + provider.getClass().getName());}try {result = provider.authenticate(authentication);if (result != null) {this.copyDetails(authentication, result);break;}} catch (AccountStatusException var13) {this.prepareException(var13, authentication);throw var13;} catch (InternalAuthenticationServiceException var14) {this.prepareException(var14, authentication);throw var14;} catch (AuthenticationException var15) {lastException = var15;}}}。。。。。}

上邊那個(gè)標(biāo)紅的provider.authenticate方法會(huì)走到DaoAuthenticationProvider對(duì)象的父類對(duì)象的authticate方法,

?

public Authentication authenticate(Authentication authentication) throws AuthenticationException {Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> {return this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported");});String username = authentication.getPrincipal() == null ? "NONE_PROVIDED" : authentication.getName();boolean cacheWasUsed = true;UserDetails user = this.userCache.getUserFromCache(username);if (user == null) {cacheWasUsed = false;try {//這塊會(huì)進(jìn)入子類DaoAuthenticationPrivoderuser = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);} catch (UsernameNotFoundException var6) {this.logger.debug("User '" + username + "' not found");if (this.hideUserNotFoundExceptions) {throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));}throw var6;}Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");}try {//校驗(yàn)是否鎖定。。(userdetails里邊的幾個(gè)返回值)this.preAuthenticationChecks.check(user);//校驗(yàn)密碼是否匹配this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);} catch (AuthenticationException var7) {if (!cacheWasUsed) {throw var7;}cacheWasUsed = false;user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication)this.preAuthenticationChecks.check(user);this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);}//后置查詢 this.postAuthenticationChecks.check(user);if (!cacheWasUsed) {this.userCache.putUserInCache(user);}Object principalToReturn = user;if (this.forcePrincipalAsString) {principalToReturn = user.getUsername();}return this.createSuccessAuthentication(principalToReturn, authentication, user);}

子類DapAuthenticationPrivoder類的

protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {this.prepareTimingAttackProtection();try {//這一塊就是調(diào)用自定義得人UserDetailsService類UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);if (loadedUser == null) {throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");} else {return loadedUser;}} catch (UsernameNotFoundException var4) {this.mitigateAgainstTimingAttack(authentication);throw var4;} catch (InternalAuthenticationServiceException var5) {throw var5;} catch (Exception var6) {throw new InternalAuthenticationServiceException(var6.getMessage(), var6);}}

進(jìn)入自定義的CusUserDetailsService

public class MyUserDetailsService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {//根據(jù)username查找用戶信息,在這,先手動(dòng)寫一個(gè)user信息log.info("查找用戶信息{}", s);BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();String password = encoder.encode("password");//user前兩個(gè)參數(shù)是進(jìn)行認(rèn)證的,第三個(gè)參數(shù)是當(dāng)前用戶所擁有的權(quán)限,security回根據(jù)授權(quán)代碼進(jìn)行驗(yàn)證return new User(s, password, true, true, true, true,AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); // AuthorityUtils.commaSeparatedStringToAuthorityList 這個(gè)方法是將一個(gè)字符一“,” 分割開} }

?

posted @ 2019-05-02 22:38 巡山小妖N 閱讀(...) 評(píng)論(...) 編輯 收藏

總結(jié)

以上是生活随笔為你收集整理的spring security 学习二的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。