如何使用SpringSecurity擴展與配置

本篇內(nèi)容主要講解“如何使用SpringSecurity擴展與配置”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“如何使用SpringSecurity擴展與配置”吧!

創(chuàng)新互聯(lián)專注于企業(yè)成都營銷網(wǎng)站建設、網(wǎng)站重做改版、綏濱網(wǎng)站定制設計、自適應品牌網(wǎng)站建設、H5網(wǎng)站設計、商城建設、集團公司官網(wǎng)建設、成都外貿(mào)網(wǎng)站建設、高端網(wǎng)站制作、響應式網(wǎng)頁設計等建站業(yè)務,價格優(yōu)惠性價比高,為綏濱等各大城市提供網(wǎng)站開發(fā)制作服務。

簡介

SpringSecurity原理(一)——初探 SpringSecurity原理(二)——認證 SpringSecurity原理(三)——授權(quán) SpringSecurity原理(四)——過濾器 SpringSecurity原理(五)——擴展與配置

如何使用SpringSecurity擴展與配置

自定義擴展

自定義Filter

自定義Filter應該是最常用的需求了,例如,為了攔截大多數(shù)的暴力登錄,我們一般會在登錄的時候給一個驗證碼,但是UsernamePasswordAuthenticationFilter沒有提供驗證碼的校驗,所以我們就可以自定義一個Filter來處理驗證碼。

又如,對于前后端分離項目,我們更多使用Token,而不是Session,也可以通過Filter來處理Token的校驗,續(xù)期的問題。

自定義Filter的方式有很多:

  1. 直接實現(xiàn)Filter

  2. 繼承GenericFilterBean

  3. 繼承OncePerRequestFilter重寫doFilterInternal

  4. 繼承BasicAuthenticationFilter重寫doFilterInternal

  5. 繼承AbstractAuthenticationProcessingFilter重寫attemptAuthentication

  6. 繼承UsernamePasswordAuthenticationFilter重寫attemptAuthentication

  7. ……

后3個都是認證相關(guān)的Filter。

因為涉及到轉(zhuǎn)發(fā)重定義等問題,一次請求Filter可能不止一次被調(diào)用,OncePerRequestFilter就是為了解決這個問題,它保證一次請求繼承它的Filter只會被調(diào)用一次。

BasicAuthenticationFilter本身繼承了OncePerRequestFilter,所以不用自己處理因為轉(zhuǎn)發(fā)等引起的Filter多次調(diào)用問題。

AbstractAuthenticationProcessingFilter添加了認證失敗,認證成功等處理,但是它沒有處理一次請求可能多次調(diào)用的問題。

對于表單認證,想偷懶,可以自己繼承UsernamePasswordAuthenticationFilter,例如,繼承UsernamePasswordAuthenticationFilter,先處理驗證碼問題,如果校驗成功,再調(diào)用UsernamePasswordAuthenticationFilter的attemptAuthentication方法。

反正自定義Filter非常靈活,根據(jù)自己的喜好選擇。

自定義了Filter如何配置呢?

最簡單的方式,自定義配置類重寫WebSecurityConfigurerAdapter的configure方法:

 @Override
protected void configure(HttpSecurity http) {
    http.addFilter(zzzFilter)
            .addFilterAfter(aaaFilter)
            .addFilterBefore(yyyFilter, UsernamePasswordAuthenticationFilter.class)
            .addFilterAt(xxxFilter,UsernamePasswordAuthenticationFilter.class);
}
  1. addFilter是添加到最后,但并不是最終的最后,因為后面的流程還會添加其他Filter

  2. addFilterAfter,添加在指定Filter之后

  3. addFilterBefore,添加在指定Filter之前

  4. addFilterAt,添加在指定Filter之前,不會覆蓋和刪除指定的Filter,感覺和addFilterBefore差不多

當然,也可以通過SecurityConfigurerAdapter的方式:

public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

    @Override
    public void configure(HttpSecurity http) {
        JwtAuthenticationFilter filter = new JwtAuthenticationFilter();
        http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
    }
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
      @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
        http.formLogin();
        http.httpBasic();
        http.apply(new JwtConfigurer());
    }
}

自定義登出成功處理器

實現(xiàn)LogoutSuccessHandler接口,一般返回json數(shù)據(jù),以便于前端給出提示信息。

public class JwtLogoutSuccessHandler implements LogoutSuccessHandler {

	@Override
	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

		if (authentication != null) {
			new SecurityContextLogoutHandler().logout(request, response, authentication);
		}

		response.setContentType("application/json;charset=UTF-8");
		ServletOutputStream outputStream = response.getOutputStream();
		outputStream.write("jwt loginout success").getBytes("UTF-8"));
		outputStream.flush();
		outputStream.close();
	}
}

配置方式:

protected void configure(HttpSecurity http){
    http..logout()
				.logoutSuccessHandler(new JwtLogoutSuccessHandler());
}

認證失敗處理器

實現(xiàn)AuthenticationFailureHandler接口,一般會返回json數(shù)據(jù),然后前端根據(jù)返回數(shù)據(jù),自己決定提示信息和跳轉(zhuǎn)。

public class LoginFailureHandler implements AuthenticationFailureHandler {

	@Override
	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {

		response.setContentType("application/json;charset=UTF-8");
		ServletOutputStream outputStream = response.getOutputStream();
		outputStream.write(JSONUtil.toJsonStr("登錄失敗").getBytes("UTF-8"));
		outputStream.flush();
		outputStream.close();
	}
}

AuthenticationSuccessHandler

實現(xiàn)AuthenticationSuccessHandler接口,如果有生成token的邏輯可以放在這里面。

public class LoginSuccessHandler implements AuthenticationSuccessHandler {

	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
		response.setContentType("application/json;charset=UTF-8");
		ServletOutputStream outputStream = response.getOutputStream();

		// 生成保存jwt等邏輯可以放這里
		outputStream.write(JSONUtil.toJsonStr("登錄成功").getBytes("UTF-8"));

		outputStream.flush();
		outputStream.close();
	}
}

當然,也可以通過繼承SimpleUrlAuthenticationSuccessHandler的方式。

配置也是老方式:

@Override
protected void configure(HttpSecurity http) throws Exception {

		http.formLogin()
				.successHandler(loginSuccessHandler)
				.failureHandler(loginFailureHandler)
}

認證異常跳轉(zhuǎn)入口

實現(xiàn)AuthenticationEntryPoint接口,這個接口在ExceptionTranslationFilter這個Filter截取到認證異常之后被調(diào)用,一般就是跳轉(zhuǎn)登錄頁,可以參考:LoginUrlAuthenticationEntryPoint

public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

	@Override
	public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
		response.setContentType("application/json;charset=UTF-8");
		response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
		ServletOutputStream outputStream = response.getOutputStream();
		outputStream.write(JSONUtil.toJsonStr("用戶名或者密碼錯誤").getBytes("UTF-8"));
		outputStream.flush();
		outputStream.close();
	}
}

授權(quán)異常處理器

實現(xiàn)AccessDeniedHandler接口,這個接口在ExceptionTranslationFilter截獲到的授權(quán)異常之后被調(diào)用。

public class JwtAccessDeniedHandler implements AccessDeniedHandler {

	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
		response.setContentType("application/json;charset=UTF-8");
		response.setStatus(HttpServletResponse.SC_FORBIDDEN);
		ServletOutputStream outputStream = response.getOutputStream();

		outputStream.write(JSONUtil.toJsonStr("您沒有操作權(quán)限,請聯(lián)系管理員").getBytes("UTF-8"));

		outputStream.flush();
		outputStream.close();

	}
}

自定義認證憑證

可以實現(xiàn)Authentication,也可以繼承AbstractAuthenticationToken。一般不需要,除非要自定義認證器。

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;

public class JwtAuthenticationToken extends AbstractAuthenticationToken {

    public JwtAuthenticationToken(Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
    }

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

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

自定義認證器

實現(xiàn)AuthenticationProvider接口。

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

public class JwtAuthenticationProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        return null;//認證流程
    }

    @Override
    public boolean supports(Class<?> authentication) {//支持校驗哪種認證憑證
        return authentication.isAssignableFrom(JwtAuthenticationToken.class);
    }
}

自定義投票者

可以實現(xiàn)AccessDecisionVoter接口,也可以直接繼承WebExpressionVoter之類的,看具體需求,一般不需要,除非要自己設計新的授權(quán)體系。

public class MyExpressionVoter extends WebExpressionVoter {
    @Override
    public int vote(Authentication authentication, FilterInvocation fi, Collection<ConfigAttribute> attributes) {
        return 1 ;//-1反對,0棄權(quán),1同意
    }
}

配置

配置WebSecurity

一般配置WebSecurity,主要是為了忽略靜態(tài)資源校驗。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers(
                "/**/*.html",
                "/public/**/*.js",
                "/public/**/*.css",
                "/public/**/*.png",
                "/**/*.gif", "/**/*.png", "/**/*.jpg", "/**/*.ico");
    }
}

匹配規(guī)則使用的是:AntPathRequestMatcher

配置HttpSecurity

HttpSecurity可以配置的東西太多了,下面是一個示例,可以參考,注意很多是重復不必要的配置,只是為了展示可以配置的內(nèi)容。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    public AccessDecisionManager accessDecisionManager(){
        List<AccessDecisionVoter<? extends Object>> decisionVoters
                = Arrays.asList(
                new WebExpressionVoter(),
                new RoleVoter(),
                new AuthenticatedVoter());
        return new ConsensusBased(decisionVoters);

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.
                // 配置登錄頁,登錄用戶名密碼參數(shù)
                formLogin().loginPage("/login")
                .passwordParameter("username").passwordParameter("password")
                // 配置登錄接口,defaultSuccessUrl配置登錄成功跳轉(zhuǎn),會跳轉(zhuǎn)到來的路徑,而successForwardUrl會跳轉(zhuǎn)固定頁面
                .loginProcessingUrl("/do-login").defaultSuccessUrl("/loginsucess")
                // 登錄失敗處理
                .failureForwardUrl("/fail").failureUrl("/failure").failureHandler(loginAuthenticationFailureHandler)
                .permitAll()
                // 配置特定url權(quán)限
                .and().authorizeRequests().antMatchers("/admin").hasRole("admin")
                // 配置鑒權(quán)管理器
                .anyRequest().authenticated().accessDecisionManager(accessDecisionManager())
//               配置登出,登錄url,登出成功處理器
                .and().logout().logoutUrl("/logout").logoutSuccessHandler(new JwtLogoutSuccessHandler())
//               關(guān)閉csrf
                .and().csrf().disable();
//      配置自定義過濾器
        http.addFilterAt(jwtAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);
//        配置鑒權(quán)失敗的處理器
        http.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler());
    }
}

幾個重要的類與接口

SecurityBuilder

public interface SecurityBuilder<O> {
	O build() throws Exception;
}

就是構(gòu)建一個特殊對象O的抽象,例如Filter。

WebSecurity就是一個為了創(chuàng)建Filter對象而設計。

HttpSecurity也是一個SecurityBuilder,不過它是為了創(chuàng)建DefaultSecurityFilterChain。

SecurityConfigurer

public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
	void init(B builder) throws Exception;
	void configure(B builder) throws Exception;
}

SecurityConfigurer目的主要有兩個:

  1. init,初始化builder,例如給一些filter設置參數(shù)

  2. configure,配置builder,例如創(chuàng)建添加新的filter

SecurityConfigurerAdapter

SecurityConfigurer的適配器,提供了init,configure的空實現(xiàn),并且添加了一個CompositeObjectPostProcessor后置處理器,主要是用來處理Filter。

AbstractHttpConfigurer

最主要添加了一個disable方法,基本上很多Filter的configure類都繼承了它。

WebSecurityConfigurer

public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> {

}

WebSecurityConfigurer主要是實例化了類型參數(shù),告訴大家,我就是配置生產(chǎn)Filter的的SecurityBuilder。

WebSecurityConfigurerAdapter

public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
}

WebSecurityConfigurerAdapter更進一步告訴大家,我的SecurityBuilder就是WebSecurity,生產(chǎn)的就是Filter。

當然WebSecurityConfigurerAdapter沒有開空頭支票,還提供了很多功能,我們自己要對SpringSecurity進行配置的話,基本都是繼承WebSecurityConfigurerAdapter。

WebSecurity

WebSecurity是一個SecurityBuilder<Filter>,所以它的主要職責之一就是創(chuàng)建Filter,重點關(guān)注它的build方法,是繼承了AbstractSecurityBuilder的build,具體邏輯在AbstractConfiguredSecurityBuilder的doBuild方法中。

@Override
protected final O doBuild() throws Exception {
    synchronized (this.configurers) {
        this.buildState = BuildState.INITIALIZING;
        beforeInit();
        init();
        this.buildState = BuildState.CONFIGURING;
        beforeConfigure();
        configure();
        this.buildState = BuildState.BUILDING;
        O result = performBuild();
        this.buildState = BuildState.BUILT;
        return result;
    }
}

很標準的模板方法模式。

正真執(zhí)行構(gòu)建的邏輯在performBuild方法中,這個方法在WebSecurity創(chuàng)建了FilterChainProxy,在HttpSecurity中創(chuàng)建了DefaultSecurityFilterChain。

HttpSecurity

前面已經(jīng)分析了,HttpSecurity的目的就一個創(chuàng)建DefaultSecurityFilterChain,注意它的performBuild方法。

到此,相信大家對“如何使用SpringSecurity擴展與配置”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!

名稱欄目:如何使用SpringSecurity擴展與配置
URL鏈接:http://muchs.cn/article30/igsppo.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供App設計移動網(wǎng)站建設、網(wǎng)站導航、網(wǎng)站建設、自適應網(wǎng)站、小程序開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

綿陽服務器托管