欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

spring security如何擴展自定義登錄

 更新時間:2024年11月14日 14:47:40   作者:小星星1991  
本文詳細介紹了Spring Security的認證原理和具體實現(xiàn),認證原理基于過濾器鏈,通過驗證用戶憑證和構(gòu)建認證對象來保護應(yīng)用程序資源,實現(xiàn)自定義認證功能的步驟包括創(chuàng)建自定義認證提供程序、實現(xiàn)UserDetailsService接口以及在配置類中進行相應(yīng)的配置

背景

spring security 默認給我們提供了一套用戶名密碼登錄的的邏輯實現(xiàn),在很多時候根本不滿足我們實際開發(fā)的要求,所以我們經(jīng)常需要對它認證模塊進行擴展,那么如何擴展我們的認證方式呢,這篇文章將會給你一個較為完整的解答。

認證原理

Spring Security使用了一種基于過濾器鏈的認證原理來保護應(yīng)用程序。它提供了一組過濾器,這些過濾器按照特定的順序?qū)γ總€請求進行處理。

認證過程中的主要步驟如下:

  1. 用戶發(fā)送認證請求到應(yīng)用程序。
  2. 應(yīng)用程序?qū)⒄埱蟀l(fā)送給Spring Security的過濾器鏈。
  3. 過濾器鏈依次處理請求,直到找到可以處理該請求的過濾器。
  4. 找到處理請求的過濾器后,該過濾器會驗證用戶的憑證(如用戶名和密碼)。
  5. 如果憑證有效,則生成一個已認證的安全上下文,并將其保存在Spring Security的上下文存儲中。
  6. 如果憑證無效,則返回認證失敗的信息給用戶。
  7. 認證成功后,用戶可以繼續(xù)訪問受保護的資源。

在認證過程中,Spring Security還提供了靈活的配置選項,可以根據(jù)特定的需求進行自定義配置,例如使用不同的認證提供程序、自定義身份驗證邏輯等。

總結(jié)起來,Spring Security的認證原理是通過一組過濾器鏈來處理認證請求,驗證用戶憑證并生成認證的安全上下文,以保護應(yīng)用程序的資源。

具體實現(xiàn)原理

Spring Security的認證實現(xiàn)原理主要涉及以下幾個核心概念和組件:

  • 用戶提供的憑證:用戶在進行認證時,需要提供其身份憑證,如用戶名和密碼。
  • 認證提供程序(Authentication Provider):認證提供程序是Spring Security中的重要組件,負責(zé)驗證用戶提供的憑證。它使用用戶提供的憑證與系統(tǒng)中存儲的憑證進行比對,并決定是否通過認證。Spring
  • Security提供了多種內(nèi)置的認證提供程序,如基于數(shù)據(jù)庫的JDBC認證提供程序、LDAP認證提供程序、InMemory認證提供程序等。同時也支持自定義認證提供程序。
  • 用戶詳情服務(wù)(UserDetailsService):用戶詳情服務(wù)是Spring Security中的接口,負責(zé)獲取用戶的詳細信息,如用戶的權(quán)限、角色等。認證提供程序在驗證用戶憑證后,會使用用戶詳情服務(wù)獲取用戶的詳細信息,并構(gòu)建一個認證對象(Authentication)。
  • 認證對象(Authentication):認證對象是Spring Security中表示已認證用戶的對象。它包含了用戶的身份信息、憑證信息、權(quán)限信息等。認證對象由認證提供程序生成,并將其保存在SecurityContextHolder的上下文存儲中。
  • 認證過濾器鏈(Authentication Filter Chain):認證過濾器鏈是Spring Security中的過濾器組成的鏈條,負責(zé)處理認證請求。每個過濾器都會對請求進行處理,并根據(jù)需要進行認證、授權(quán)和其他安全操作。在認證過程中,認證過濾器鏈會根據(jù)請求的URL匹配合適的過濾器進行處理。
  • 安全上下文(Security Context):安全上下文是Spring Security中用于保存已認證用戶信息的容器。它以ThreadLocal的方式存儲,可以通過SecurityContextHolder來訪問和操作。安全上下文中保存了當前的認證對象,可以在應(yīng)用程序的任何地方獲取已認證用戶的信息。

總結(jié)起來,Spring Security的認證實現(xiàn)原理是通過認證提供程序驗證用戶提供的憑證,使用用戶詳情服務(wù)獲取用戶的詳細信息,并構(gòu)建一個認證對象,然后將該認證對象保存在安全上下文中。

認證過濾器鏈負責(zé)處理認證請求,并根據(jù)需要進行認證、授權(quán)和其他安全操作。

通過這種方式,Spring Security實現(xiàn)了對應(yīng)用程序進行認證和授權(quán)的功能。

如何實現(xiàn)自定義認證的功能

要自定義Spring Security的認證模塊,你可以按照以下步驟進行:

  • 創(chuàng)建一個自定義的認證提供程序(Authentication Provider)類,該類實現(xiàn)了org.springframework.security.authentication.AuthenticationProvider接口。在該類中,你可以編寫你自己的認證邏輯,比如驗證用戶名和密碼是否匹配。
  • 實現(xiàn)UserDetailsService接口來獲取用戶的詳細信息。你可以根據(jù)自己的需求,從數(shù)據(jù)庫、LDAP等數(shù)據(jù)源中獲取用戶信息,并返回一個UserDetails對象。
  • 在配置類(通常是繼承自WebSecurityConfigurerAdapter的類)中,覆蓋configure(AuthenticationManagerBuilder auth)方法。在該方法中,你可以指定使用你自定義的認證提供程序和用戶詳情服務(wù)來進行認證。
  • 在上述配置類中,覆蓋configure(HttpSecurity http)方法來配置認證過濾器鏈。你可以根據(jù)需要添加和配置各種過濾器,例如表單登錄過濾器、基于Token的認證過濾器等。

下面根據(jù)微信小程序登錄來實現(xiàn)一個自定義模塊的接口:

定義token信息類

public class JsCodeAuthenticationToken extends AbstractAuthenticationToken {
    private static final long serialVersionUID = 5615450055779559101L;
    private String code;

    /**
     * Creates a token with the supplied array of authorities.
     *
     * @param authorities the collection of <tt>GrantedAuthority</tt>s for the principal
     *                    represented by this authentication object.
     */
    public JsCodeAuthenticationToken(Collection<? extends GrantedAuthority> authorities, String code) {
        super(authorities);
        this.code = code;
    }

    public JsCodeAuthenticationToken(String code) {
        this(Collections.emptyList(), code);
    }

    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return code;
    }

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException(
                    "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        }

        super.setAuthenticated(false);
    }

    @Override
    public void eraseCredentials() {
        super.eraseCredentials();
    }
}

從代碼中我們可以看出我們需要創(chuàng)建一個相關(guān)的token類來繼承 AbstractAuthenticationToken 類,并實現(xiàn)相關(guān)的方法

AbstractAuthenticationToken 的定義

  • Spring Security框架中的一個抽象類,用于表示身份驗證請求令牌。它用于封裝用戶憑據(jù)或其他必要的信息來進行用戶身份驗證。
  • AbstractAuthenticationToken的子類負責(zé)提供特定的身份驗證細節(jié),例如用戶名和密碼、基于令牌的身份驗證令牌等。這些子類通常包含獲取器和設(shè)置器,用于訪問和修改身份驗證細節(jié)。
  • AbstractAuthenticationToken還提供了一組方法來管理身份驗證狀態(tài),例如設(shè)置已認證標志、檢索與已認證用戶關(guān)聯(lián)的權(quán)限以及管理與身份驗證過程相關(guān)的其他詳細信息。

AbstractAuthenticationToken 的工作原理

  • 當用戶請求進行身份驗證時,Spring Security將創(chuàng)建一個實現(xiàn)AbstractAuthenticationToken的具體子類對象,并將其中的身份驗證信息進行填充。然后,該身份驗證令牌將被傳遞給身份驗證管理器進行進一步的身份驗證處理。
  • 身份驗證管理器將檢查身份驗證令牌,并根據(jù)所配置的身份驗證策略和安全規(guī)則,對用戶進行身份驗證。在身份驗證過程中,身份驗證管理器可能會調(diào)用相關(guān)的身份驗證提供者,比如用戶名密碼身份驗證提供者或令牌身份驗證提供者,來驗證用戶的憑據(jù)信息。
  • 一旦用戶通過身份驗證,身份驗證管理器將更新AbstractAuthenticationToken中的已認證標志和權(quán)限信息,并將其返回給應(yīng)用程序。在后續(xù)的請求中,應(yīng)用程序可以根據(jù)需要訪問身份驗證令牌中的身份驗證信息和權(quán)限信息,以實現(xiàn)不同的業(yè)務(wù)邏輯和訪問控制。

定義一個接收參數(shù)的filter

@Slf4j
public class JsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    private static String jsCode = "code";

    private static boolean postOnly = true;

    protected JsCodeAuthenticationFilter(WechatProperties properties) {
        super(new AntPathRequestMatcher(properties.getAuthUrl(), "POST"));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        if (postOnly && !request.getMethod().equalsIgnoreCase("post")) {
            throw new AuthenticationServiceException("該接口不支持:" + request.getMethod());
        }

        String code = obtainCode(request);
        code = code.trim();
        log.info("得到的code:{}", code);
        JsCodeAuthenticationToken authenticationTokenRequest = new JsCodeAuthenticationToken(code);
        setDetails(request, authenticationTokenRequest);
        return this.getAuthenticationManager().authenticate(authenticationTokenRequest);
    }

    public String obtainCode(HttpServletRequest request) {
        if (request.getHeader("Content-Type").contains("application/json")) {
            return obtainCodeJson(request, jsCode);
        }

        return request.getParameter(jsCode);
    }

    public String obtainCodeJson(HttpServletRequest request, String param) {
        StringBuilder builder = new StringBuilder();
        String line = null;
        String code = null;
        try{
            BufferedReader reader = request.getReader();
            while ((line = reader.readLine()) != null) {
                builder.append(line);
            }

            Map<String, String> map = JsonUtils.fromString(builder.toString(), Map.class, String.class, String.class);
            code = map.get(param);
        }catch (RuntimeException | IOException e) {
            throw new AuthenticationServiceException("獲取參數(shù)失敗");
        }

        return code;
    }

    /**
     * Provided so that subclasses may configure what is put into the
     * authentication request's details property.
     *
     * @param request     that an authentication request is being created for
     * @param authRequest the authentication request object that should have its details
     *                    set
     */
    protected void setDetails(HttpServletRequest request, JsCodeAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

從上面的代碼可以看出,我們需要定義一個filter 類繼承 AbstractAuthenticationProcessingFilter 來獲取前端傳輸?shù)膮?shù)

AbstractAuthenticationProcessingFilter的定義

  • AbstractAuthenticationProcessingFilter是Spring Security框架中的一個過濾器,用于處理用戶認證的相關(guān)操作。

AbstractAuthenticationProcessingFilter的功能有哪些

  • 攔截用戶發(fā)起的認證請求,并交給AuthenticationManager來進行身份驗證。
  • 通過調(diào)用AuthenticationManager完成身份驗證后,將認證結(jié)果封裝成Authentication對象。
  • 將認證結(jié)果傳遞給AbstractAuthenticationProcessingFilter的成功或失敗處理器進行處理。
  • 處理認證成功或失敗的邏輯,例如生成并返回認證成功的Jwt token、跳轉(zhuǎn)到特定頁面、返回錯誤信息等。

定義相關(guān)的Provider 類

public class JsCodeAuthenticationProvider implements AuthenticationProvider {

    private WxOAuth2Service weChatService;

    private UserDetailsService userDetailsService;

    private WxMpProperties wxMpProperties;

    public JsCodeAuthenticationProvider(WxOAuth2Service weChatService, WxMpProperties wxMpProperties) {
        this.weChatService = weChatService;
        this.wxMpProperties = wxMpProperties;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        JsCodeAuthenticationToken token = (JsCodeAuthenticationToken) authentication;
        String code = (String) token.getPrincipal();
        log.info("得到的code:{}", code);
        if (StringUtils.isBlank(code)) {
            throw new AuthenticationServiceException("jscode 不能為空");
        }

        WxOAuth2AccessToken wxOAuth2AccessToken;
        try {
            wxOAuth2AccessToken = weChatService.getAccessToken(wxMpProperties.getAppId(), wxMpProperties.getSecret(), code);
        } catch (WxErrorException e) {
            throw new AuthenticationServiceException("授權(quán)登錄失敗");
        }



        WeChatDetailsService weChatDetailsService = (WeChatDetailsService)userDetailsService;
        UserDetails details = weChatDetailsService.loadByToken(wxOAuth2AccessToken.getOpenId(), wxOAuth2AccessToken.getAccessToken());
        if (details == null) {
            throw new AuthenticationServiceException("無法獲取公眾號用戶");
        }

        JsCodeAuthenticationToken authenticationResult = new JsCodeAuthenticationToken(details.getUsername());
        authenticationResult.setDetails(token.getDetails());
        return authenticationResult;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return JsCodeAuthenticationToken.class.isAssignableFrom(authentication);
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }
}

AuthenticationProvider 的定義

AuthenticationProvider是Spring Security框架中的一個接口,用于身份驗證的核心組件。

它的定義如下:

public interface AuthenticationProvider {
 Authentication authenticate(Authentication authentication) throws AuthenticationException;
 boolean supports(Class<?> authentication);
}

它包含兩個方法:

  • authenticate(Authentication authentication):該方法用于執(zhí)行身份驗證過程。輸入?yún)?shù)authentication是一個封裝了用戶認證信息的Authentication對象,包括用戶名、密碼等信息。該方法返回一個認證成功的Authentication對象,或者拋出AuthenticationException異常表示認證失敗。
  • supports(Class<?> authentication):該方法用于判斷是否支持給定類型的認證請求。輸入?yún)?shù)authentication是要被驗證的對象類型,通常是UsernamePasswordAuthenticationToken或JwtAuthenticationToken等。該方法返回一個boolean值,表示是否支持該類型的認證請求。

AuthenticationProvider可以自定義實現(xiàn),用于實現(xiàn)不同的身份驗證邏輯。開發(fā)者可以通過實現(xiàn)該接口,并覆蓋其中的方法來定制化身份驗證過程,例如從數(shù)據(jù)庫查詢用戶信息并進行密碼校驗等。

在Spring Security的配置中,可以通過AuthenticationManagerBuilder來注冊和配置AuthenticationProvider的實例,以供身份驗證使用。

定義相關(guān)配置類

@Component
@EnableConfigurationProperties(WxMpProperties.class)
public class JsCodeAuthenticationSecurityConfig  extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private AuthenticationSuccessHandler jsCodeAuthenticationSuccessHandler;

    @Autowired
    private AuthenticationFailureHandler jsCodeAuthenticationFailureHandler;

    @Autowired
    private WxOAuth2Service weChatService;

    @Autowired
    private WechatProperties weChatProperties;

    @Autowired
    private WxMpProperties wxMpProperties;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        JsCodeAuthenticationFilter mobilePasswordAuthenticationFilter = new JsCodeAuthenticationFilter(weChatProperties);
        mobilePasswordAuthenticationFilter.setAuthenticationSuccessHandler(jsCodeAuthenticationSuccessHandler);
        mobilePasswordAuthenticationFilter.setAuthenticationFailureHandler(jsCodeAuthenticationFailureHandler);
        mobilePasswordAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
        JsCodeAuthenticationProvider provider = new JsCodeAuthenticationProvider(weChatService, wxMpProperties);
        provider.setUserDetailsService(userDetailsService);
        // 將當前服務(wù)注冊到 mobilePasswordAuthenticationFilter 連之后
        http.authenticationProvider(provider).addFilterAfter(mobilePasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

SecurityConfigurerAdapter 的定義

SecurityConfigurerAdapter是Spring Security提供的一個抽象類,用于簡化配置和定制化Spring Security的行為。

它是一個適配器模式,繼承自SecurityConfigurer接口,并提供了一些默認實現(xiàn)方法,用于提供給開發(fā)者靈活地配置和擴展Spring Security。

SecurityConfigurerAdapter的定義如下:

public abstract class SecurityConfigurerAdapter<O extends SecurityFilterChain, B extends SecurityBuilder<O>> implements SecurityConfigurer<O, B> {
 // 提供一個空實現(xiàn)的configure方法,供子類進行覆蓋定制
  public void configure(B builder) throws Exception {}
 // 提供一個空實現(xiàn)的init方法,供子類進行覆蓋定制 
 public void init(B builder) throws Exception {}
 // 提供一個空實現(xiàn)的configure方法,供子類進行覆蓋定制 
 public void configure(O object) throws Exception {}
 // 提供一個空實現(xiàn)的postProcess方法,供子類進行覆蓋定制 
 protected void postProcess(O object) throws Exception {}
}

開發(fā)者可以繼承SecurityConfigurerAdapter,并重寫其中的方法來實現(xiàn)自定義的安全配置。常見的用法是在WebSecurityConfigurerAdapter中繼承SecurityConfigurerAdapter,并重寫configure方法來配置Spring Security的行為,例如定義認證規(guī)則、權(quán)限控制等。通過繼承SecurityConfigurerAdapter可以避免直接實現(xiàn)SecurityConfigurer接口時需要實現(xiàn)所有方法的繁瑣操作。

整體配置

    public void configure(HttpSecurity http) throws Exception {
        String[] auth = StringUtils.split( securityProperties.getAuthUrl(), ",");
        http.formLogin().failureHandler(authenticationFailureHandler)
                .successHandler(authenticationSuccessHandler)
                .loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM)
                .loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)
                .and().apply(jsCodeAuthenticationSecurityConfig)
                .and().apply(usernameAuthenticationSecurityConfig())
                .and().authorizeRequests().antMatchers(auth).authenticated()
                .and().authorizeRequests().anyRequest().permitAll()
                .and().csrf().disable();
    }

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 常用的ResponseEntity.BodyBuilder和自定義ResponseEntity的實例

    常用的ResponseEntity.BodyBuilder和自定義ResponseEntity的實例

    這篇文章主要介紹了常用的ResponseEntity.BodyBuilder和自定義ResponseEntity的實例,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • java壓縮文件與刪除文件的示例代碼

    java壓縮文件與刪除文件的示例代碼

    這篇文章主要介紹了java壓縮文件與刪除文件的示例代碼,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • 深入了解SpringBoot中@ControllerAdvice的介紹及三種用法

    深入了解SpringBoot中@ControllerAdvice的介紹及三種用法

    這篇文章主要為大家詳細介紹了SpringBoot中@ControllerAdvice的介紹及三種用法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-02-02
  • Mybatis的TypeHandler加解密數(shù)據(jù)實現(xiàn)

    Mybatis的TypeHandler加解密數(shù)據(jù)實現(xiàn)

    在我們數(shù)據(jù)庫中有些時候會保存一些用戶的敏感信息,所以就需要對這些數(shù)據(jù)進行加密,那么本文就介紹了Mybatis的TypeHandler加解密數(shù)據(jù)實現(xiàn),感興趣的可以了解一下
    2021-06-06
  • java出現(xiàn)no XXX in java.library.path的解決及eclipse配置方式

    java出現(xiàn)no XXX in java.library.path的解決及eclipse配

    這篇文章主要介紹了java出現(xiàn)no XXX in java.library.path的解決及eclipse配置方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • java靜態(tài)工具類注入service出現(xiàn)NullPointerException異常處理

    java靜態(tài)工具類注入service出現(xiàn)NullPointerException異常處理

    如果我們要在我們自己封裝的Utils工具類中或者非controller普通類中使用@Autowired注解注入Service或者Mapper接口,直接注入是報錯的,因Utils用了靜態(tài)方法,我們無法直接用非靜態(tài)接口的,遇到這問題,我們要想法解決,下面小編就簡單介紹解決辦法,需要的朋友可參考下
    2021-09-09
  • 解決Mybatis-Plus操作分頁后數(shù)據(jù)失效問題

    解決Mybatis-Plus操作分頁后數(shù)據(jù)失效問題

    這篇文章主要介紹了解決Mybatis-Plus操作分頁后數(shù)據(jù)失效問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • 最新評論