Spring Security系列(一):自定义AuthenticationFilter

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于Spring的应用程序的事实标准。 

Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements.

Spring Security是一个专注于为Java应用程序提供身份验证和授权的框架。与所有Spring项目一样,Spring Security的真正威力在于它可以非常容易地扩展以满足定制需求。

Spring Security的核心是一组过滤器链,WebSecurityConfiguration配置类中定义了过滤器链

/**
 * 使用WebSecurity创建FilterChainProxy
 * FilterChainProxy执行基于web的Spring Security安全性
 */
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {

    @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		if (!hasConfigurers) {
			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			webSecurity.apply(adapter);
		}
		return webSecurity.build();
	}
}

实际项目可以通过继承WebSecurityConfigurerAdapter并重写configure(HttpSecurity http)方法自定义过滤器链的生成,然后通过configure(AuthenticationManagerBuilder auth)方法添加AuthenticationProvider自定义类实现认证逻辑的自定义。

@EnableWebSecurity
@EnableGlobalMethodSecurity(
        prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

       @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private TokenAuthenticationProvider tokenAuthenticationProvider;

    /**
     * 重写此方法以配置HttpSecurity
     * 默认配置:http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
     *
     * @param http
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)/*匿名用户访问无权限资源时的异常*/
                //.accessDeniedHandler() /*认证过的用户访问无权限资源时的异常*/
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) /*禁用Session*/
                .and()
                .authorizeRequests()
                .antMatchers("/token").permitAll()
                .anyRequest().authenticated();
        //添加自定义过滤器 FilterComparator 类标识了过滤器的执行顺序
        //自定义认证过滤器需要放置在UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(tokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * 自定义AuthenticationManager
     *
     * 自定义AuthenticateFilter(例如JWT token校验)之后,
     * 需要提供对应的支持AbstractAuthenticationToken的AuthenticationProvider
     * @param auth
     * @throws Exception
     */
    @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(tokenAuthenticationProvider);
    }

    @Bean
    public TokenAuthenticationProcessingFilter tokenFilter() {
        return new TokenAuthenticationProcessingFilter();
    }
}

自定义TokenAuthenticationProcessingFilter

public class TokenAuthenticationProcessingFilter extends OncePerRequestFilter {

    public static final String AUTHORIZATION = "Authorization";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
            FilterChain filterChain) throws ServletException, IOException {
        String authToken = request.getHeader(AUTHORIZATION);
        if(!StringUtils.isEmpty(authToken)){
            authToken = authToken.startsWith("Bearer ") ? authToken.split(" ")[1] : authToken;
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(authToken, "");
            //自定义Filter的核心方法 绑定AuthenticationToken至SecurityContext
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        filterChain.doFilter(request, response);
    }
}

AuthenticationProvider的实现类需要重写自定义认证逻辑方法和支持AbstractAuthenticationToken的类型

@Component
public class TokenAuthenticationProvider implements AuthenticationProvider {
    @Override public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        String token = (String) authentication.getPrincipal();
        UsernamePasswordAuthenticationToken result = null;
        //token校验 可以是JWT token 签名的校验
        if ("auth-token".equals(token)) {
         /*校验成功 写入Collection<? extends GrantedAuthority> authorities
          * 使用样例 .antMatchers("/user/**").hasRole("user")
          */
            result = new UsernamePasswordAuthenticationToken(token, authentication.getCredentials(),
                    Collections.singletonList(() -> "user"));
        }
        return result;
    }

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值