7. Spring Security(二)-- WebSecurityConfigurer配置以及filter顺序

建议阅读掘金博客:https://juejin.im/post/5d0b1eb35188252f921b1535#heading-3

1. WebSecurityConfigurerAdapter

  在使用WebSecurityConfigurerAdapter前,先了解Spring security config。
  Spring security config具有三个模块,一共有3个builder,认证相关的AuthenticationManagerBuilder和web相关的WebSecurity、HttpSecurity。

  1. AuthenticationManagerBuilder:用来配置全局的认证相关的信息,其实就是AuthenticationProvider和UserDetailsService,前者是认证服务提供商,后者是用户详情查询服务;

  2. WebSecurity: 全局请求忽略规则配置(比如说静态文件,比如说注册页面)、全局HttpFirewall配置、是否debug配置、全局SecurityFilterChain配置、privilegeEvaluator、expressionHandler、securityInterceptor;

  3. HttpSecurity:具体的权限控制规则配置。一个这个配置相当于xml配置中的一个标签。各种具体的认证机制的相关配置,OpenIDLoginConfigurer、AnonymousConfigurer、FormLoginConfigurer、HttpBasicConfigurer等。

      WebSecurityConfigurerAdapter提供了简洁方式来创建WebSecurityConfigurer,其作为基类,可通过实现该类自定义配置类,主要重写这三个方法:

 protected void configure(AuthenticationManagerBuilder auth) throws Exception {}
 public void configure(WebSecurity web) throws Exception {}
 protected void configure(HttpSecurity httpSecurity) throws Exception {}

     而且其自动从SpringFactoriesLoader查找AbstractHttpConfigurer让我们去扩展,想要实现必须创建一个AbstractHttpConfigurer的扩展类,并在classpath路径下创建一个文件META-INF/spring.factories。例如:

            org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyClassThatExtendsAbstractHttpConfigurer

其源码分析:

//1.init初始化:获取HttpSecurity和配置FilterSecurityInterceptor拦截器到WebSecurity
 public void init(final WebSecurity web) throws Exception {
         //获取HttpSecurity
    final HttpSecurity http = getHttp();
 	//配置FilterSecurityInterceptor拦截器到WebSecurity
     web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
     	public void run() {
 	    	FilterSecurityInterceptor securityInterceptor = http
 			    	.getSharedObject(FilterSecurityInterceptor.class);
 	    	web.securityInterceptor(securityInterceptor);
 	    }
     });
 }
 ......
 //2.获取HttpSecurity的过程
 protected final HttpSecurity getHttp() throws Exception {
 if (http != null) {
 	return http;
 }

 DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
 		.postProcess(new DefaultAuthenticationEventPublisher());
 localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);

 AuthenticationManager authenticationManager = authenticationManager();
 authenticationBuilder.parentAuthenticationManager(authenticationManager);
 Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();

 http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
 		sharedObjects);
 if (!disableDefaults) {
 	// 默认的HttpSecurity的配置
 	http
                 //添加 CSRF 支持,使用WebSecurityConfigurerAdapter时,默认启用,禁用csrf().disable()
 		.csrf().and() 
 		//添加WebAsyncManagerIntegrationFilter
 		.addFilter(new WebAsyncManagerIntegrationFilter())
 		//允许配置异常处理
 		.exceptionHandling().and()
 		//将安全标头添加到响应
 		.headers().and()
 		//允许配置会话管理
 		.sessionManagement().and()
 		//HttpServletRequest之间的SecurityContextHolder创建securityContext管理
 		.securityContext().and()
 		//允许配置请求缓存
 		.requestCache().and()
 		//允许配置匿名用户
 		.anonymous().and()
 		//HttpServletRequestd的方法和属性注册在SecurityContext中
 		.servletApi().and()
 		//使用默认登录页面
 		.apply(new DefaultLoginPageConfigurer<>()).and()
 		//提供注销支持
 		.logout();
 	// @formatter:on
 	ClassLoader classLoader = this.context.getClassLoader();
 	List<AbstractHttpConfigurer> defaultHttpConfigurers =
 			SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

 	for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
 		http.apply(configurer);
 	}
 }
 configure(http);
 return http;
 }
 ...
 //3.可重写方法实现自定义的HttpSecurity   
 protected void configure(HttpSecurity http) throws Exception {
 logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");

 http
 	.authorizeRequests()
 		.anyRequest().authenticated()
 		.and()
 	.formLogin().and()
 	.httpBasic();
 }
 ....

从源码init初始化模块中的“获取HttpSecurity”和“配置FilterSecurityInterceptor拦截器到WebSecurity”中可以看出,想要spring Security如何知道我们要求所有用户都经过身份验证? Spring Security如何知道我们想要支持基于表单的身份验证?只要重写protected void configure(HttpSecurity http) throws Exception方法即可。因此我们需要理解HttpSecurity的方法的作用,如何进行配置。下一节来讨论HttpSecurity。

使用.permitAll()将配置授权,以便在该特定路径上允许所有请求(来自匿名用户和登录用户).

.anonymous()表达式主要是指用户的状态(已登录或未登录).
基本上,直到用户被“认证”为止,它就是“匿名用户”.就像每个人都有“默认角色”一样.

2. HttpSecurity

  • HttpSecurity基于Web的安全性允许为特定的http请求进行配置。其有很多方法,列举一些常用的如下表:
方法说明使用案例
csrf()添加 CSRF 支持,使用WebSecurityConfigurerAdapter时,默认启用禁用:csrf().disable()
openidLogin()用于基于 OpenId 的验证openidLogin().permitAll();
authorizeRequests()开启使用HttpServletRequest请求的访问限制authorizeRequests().anyRequest().authenticated()
formLogin()开启表单的身份验证,如果未指定FormLoginConfigurer#loginPage(String),则将生成默认登录页面formLogin().loginPage("/authentication/login").failureUrl("/authentication/login?failed")
oauth2Login()开启OAuth 2.0或OpenID Connect 1.0身份验证authorizeRequests()..anyRequest().authenticated()..and().oauth2Login()
rememberMe()开启配置“记住我”的验证authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin().permitAll().and().rememberMe()
addFilter()添加自定义的filteraddFilter(new CustomFilter())
addFilterAt()在指定filter相同位置上添加自定义filteraddFilterAt(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
addFilterAfter()在指定filter位置后添加自定义filteraddFilterAfter(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
requestMatchers()开启配置HttpSecurity,仅当RequestMatcher相匹配时开启requestMatchers().antMatchers("/api/**")
antMatchers()其可以与authorizeRequests()、RequestMatcher匹配,如:requestMatchers().antMatchers("/api/**") 
logout()添加退出登录支持。当使用WebSecurityConfigurerAdapter时,这将自动应用。默认情况是,访问URL”/ logout”,使HTTP Session无效来清除用户,清除已配置的任何#rememberMe()身份验证,清除SecurityContextHolder,然后重定向到”/login?success”logout().deleteCookies("remove").invalidateHttpSession(false).logoutUrl("/custom-logout").logoutSuccessUrl("/logout-success");

HttpSecurity还有很多方法供我们使用,去配置HttpSecurity。由于太多这边就不一一说明,有兴趣可去研究。

/**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */


 @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception
    {
        httpSecurity
                // CRSF禁用,因为不使用session
                .csrf().disable()
                // 认证失败处理类
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 过滤请求
                .authorizeRequests()
                // 对于登录login 验证码captchaImage 允许匿名访问
                .antMatchers("/login", "/captchaImage").anonymous()
                .antMatchers(
                        HttpMethod.GET,
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                ).permitAll()
                .antMatchers("/profile/**").anonymous()
                .antMatchers("/common/download**").anonymous()
                .antMatchers("/common/download/resource**").anonymous()
                .antMatchers("/swagger-ui.html").anonymous()
                .antMatchers("/swagger-resources/**").anonymous()
                .antMatchers("/webjars/**").anonymous()
                .antMatchers("/*/api-docs").anonymous()
                .antMatchers("/druid/**").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                .headers().frameOptions().disable();
        httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
        // 添加JWT filter
        httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

3. WebSecurityConfigurerAdapter使用

WebSecurityConfigurerAdapter示例:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 @Autowired
 private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
 protected void configure(HttpSecurity http) throws Exception {    
     http
     //request 设置
     .authorizeRequests()   //http.authorizeRequests() 方法中的自定义匹配
     .antMatchers("/resources/**", "/signup", "/about").permitAll() // 指定所有用户进行访问指定的url  ,permitAll()表示这个不需要验证 静态资源链接 登录页面,登录失败页面   
     .antMatchers("/admin/**").hasRole("ADMIN")  //指定具有特定权限的用户才能访问特定目录,hasRole()方法指定用户权限,且不需前缀 “ROLE_“  
     .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")//          
     .anyRequest().authenticated()  //任何请求没匹配的都需要进行验证                                           
     .and()        //login设置  自定义登录页面且允许所有用户登录
     .formLogin()      
     .loginPage("/login") //The updated configuration specifies the location of the log in page  指定自定义登录页面
     .permitAll(); // 允许所有用户访问登录页面. The formLogin().permitAll() 方法
     .and 
     .logout()  //logouts 设置                                                              
     .logoutUrl("/my/logout")  // 指定注销路径                                              
     .logoutSuccessUrl("/my/index") //指定成功注销后跳转到指定的页面                                        
     .logoutSuccessHandler(logoutSuccessHandler)  //指定成功注销后处理类 如果使用了logoutSuccessHandler()的话, logoutSuccessUrl()就会失效                                
     .invalidateHttpSession(true)  // httpSession是否有效时间,如果使用了 SecurityContextLogoutHandler,其将被覆盖                                        
     .addLogoutHandler(logoutHandler)  //在最后增加默认的注销处理类LogoutHandler                
     .deleteCookies(cookieNamesToClear);//指定注销成功后remove cookies
     //增加在FilterSecurityInterceptor前添加自定义的myFilterSecurityInterceptor
     http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
   }

4. filter顺序

Spring Security filter顺序:

Filter Class说明
ChannelProcessingFilter访问协议控制过滤器,可能会将我们重新定向到另外一种协议,从http转换成https
SecurityContextPersistenceFilter创建SecurityContext安全上下文信息和request结束时清空SecurityContextHolder
ConcurrentSessionFilter并发访问控制过滤器,主要功能:SessionRegistry中获取SessionInformation来判断session是否过期,从而实现并发访问控制。
HeaderWriterFilter给http response添加一些Header
CsrfFilter跨域过滤器,跨站请求伪造保护Filter
LogoutFilter处理退出登录的Filter
X509AuthenticationFilter添加X509预授权处理机制支持
CasAuthenticationFilter认证filter,经过这些过滤器后SecurityContextHolder中将包含一个完全组装好的Authentication对象,从而使后续鉴权能正常执行
UsernamePasswordAuthenticationFilter认证的filter,经过这些过滤器后SecurityContextHolder中将包含一个完全组装好的Authentication对象,从而使后续鉴权能正常执行。表单认证是最常用的一个认证方式。
BasicAuthenticationFilter认证filter,经过这些过滤器后SecurityContextHolder中将包含一个完全组装好的Authentication对象,从而使后续鉴权能正常执行
SecurityContextHolderAwareRequestFilter此过滤器对ServletRequest进行了一次包装,使得request具有更加丰富的API
JaasApiIntegrationFilter(JAAS)认证方式filter
RememberMeAuthenticationFilter记忆认证处理过滤器,即是如果前面认证过滤器没有对当前的请求进行处理,启用了RememberMe功能,会从cookie中解析出用户,并进行认证处理,之后在SecurityContextHolder中存入一个Authentication对象。
AnonymousAuthenticationFilter匿名认证处理过滤器,当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到SecurityContextHolder中
SessionManagementFilter会话管理Filter,持久化用户登录信息,可以保存到session中,也可以保存到cookie或者redis中
ExceptionTranslationFilter异常处理过滤器,主要拦截后续过滤器(FilterSecurityInterceptor)操作中抛出的异常。
FilterSecurityInterceptor安全拦截过滤器类,获取当前请求url对应的ConfigAttribute,并调用accessDecisionManager进行访问授权决策。

spring security的默认filter链:

 SecurityContextPersistenceFilter
->HeaderWriterFilter
->LogoutFilter
->UsernamePasswordAuthenticationFilter
->RequestCacheAwareFilter
->SecurityContextHolderAwareRequestFilter
->SessionManagementFilter
->ExceptionTranslationFilter
->FilterSecurityInterceptor

5. 引入security 后跳转默认登陆页解决

springboot 2.x 开发调试禁用spring security

取消springsecurity默认的登录验证

开发调试时,不需要每次都登陆,所以要禁用

5.1 启动类排除自动装配类

这样是去除了security 的功能

@SpringBootApplication(exclude = {
        org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class,
        //引入 'org.springframework.boot:spring-boot-starter-actuator'依赖后,也要排除下方的自动装配类
        org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration.class
})
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

5.2.配置文件排除自动装配类

application.yml配置文件写法

spring:
  autoconfigure:
    #跳过security自动配置
    exclude:
    - org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
    - org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration

application.properties配置文件写法

spring.autoconfigure.exclude[0]=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
spring.autoconfigure.exclude[1]=org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration

5.3 使用配置文件的办法(*)

@PreAuthorize 可以正常使用·

@Configuration
// @EnableGlobalMethodSecurity(prePostEnabled = true) 开启后,Spring Security 的 @PreAuthorize,@PostAuthorize 注解才可以使用。
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(101)
public class SecurityConfig
    extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {

        http
            .csrf()
            .disable()
            .authorizeRequests()
            .anyRequest()
            .permitAll()
            .and()
            .logout()
            .permitAll();
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值