Spring security 认证的配置使用以及自定义组件

标签:

本文出自jvm123.com-java技术分享站:http://jvm123.com/2019/12/spring-security.html

Spring security 以及 shiro 等权限框架,一般都有两部分功能:认证和鉴权,也就是用户登陆和权限控制。本文记录spring security 在 用户身份认证时的配置使用方法,以及自定义认证组件的方法。

Spring security 的配置首先继承WebSecurityConfigurerAdapter,下面时一个简单的配置示例:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin().loginPage("/login")
            .defaultSuccessUrl("/index.html").permitAll()
            .and()
            .logout().permitAll();
        super.configure(http);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("yawn1").password("111").roles("USER")
                .and()
                .withUser("yawn2").password("123456").roles("USER");
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/static/**");
        super.configure(web);
    }
}

如果需要自定义组件和认证,可如下配置:

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

    private String[] authenticated = {"/**"};
    private String[] permits = {"/css/**", "/js/**", "/images/**"};

    private boolean iframeEnabled = true;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 权限的配置
        http.authorizeRequests()
                .antMatchers(permits).permitAll()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/web/**").hasAuthority("user")
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers(authenticated).authenticated();
        // 禁用csrf
        http.csrf().disable();

        // iframe的配置
        if (iframeEnabled) {
            http.headers()
                    .frameOptions()
                    .sameOrigin()
                    .cacheControl();
        }

        // 登陆注销的配置
        http.formLogin()
                .loginPage("/login")
                .permitAll()
                .failureHandler(authenticationFailureHandler())
                .successHandler(authenticationSuccessHandler())
                .defaultSuccessUrl("app.html")
                .and()
                .logout()
                .logoutUrl("logout")
                .logoutSuccessUrl("login");

        // 添加自定义过滤器
        http.addFilterBefore(new KaptchaFilter(), CsrfFilter.class);
        // 重写认证逻辑
        http.authenticationProvider(authenticationProvider());
        // 认证异常的处理逻辑
        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    AuthenticationFailureHandler authenticationFailureHandler() {
        return new LoginFailHandler();
    }

    @Bean
    AuthenticationSuccessHandler authenticationSuccessHandler() {
        return new LoginSuccessHandler();
    }

    @Bean
    AuthenticationProvider authenticationProvider() {
        return new SqlAuthenticationProvider();
    }

    @Bean
    AuthenticationEntryPoint authenticationEntryPoint() {
        return new AjaxAuthenticationEntryPoint();
    }

}

其中实现AuthenticationProvider 可以自定义认证的逻辑。 AuthenticationProvider 中有两个方法需要实现,如下:

public class SqlAuthenticationProvider implements AuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // ...
        renturn authentication;
    }

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

在ProviderManager的authenticate方法中,轮询成员变量List<AuthenticationProvider> providers。该providers中如果有一个 AuthenticationProvider的supports函数返回true,那么就会调用该AuthenticationProvider的authenticate函数认证,如果认证成功则整个认证过程结束。如果不成功,则继续使用下一个合适的AuthenticationProvider进行认证,只要有一个认证成功则为认证成功。

完整的认证逻辑为:

  1. 在UsernamePasswordAuthenticationFilter的attemptAuthentication()方法中,调用AuthenticationManager进行认证
  2. AuthenticationManager接收Authentication对象作为参数,并通过authenticate方法对其进行验证(实际由其实现类ProviderManager完成)
  3. 在ProviderManager的authenticate方法中,轮训成员变量List<AuthenticationProvider> providers。该providers中如果有一个AuthenticationProvider的supports函数返回true,那么就会调用该AuthenticationProvider的authenticate函数认证,如果认证成功则整个认证过程结束。如果不成功,则继续使用下一个合适的AuthenticationProvider进行认证,只要有一个认证成功则为认证成功。
  4. UsernamePasswordAuthenticationToken实现了Authentication,主要是将用户输入的用户名密码进行封装,并提供给AuthenticationManager进行验证,验证成功后,返回一个认证成功的UsernamePasswordAuthenticationToken对象

发表评论