SpringSecurity自定义AuthenticationProvider和AuthenticationFilter

摘要:
密码编码器。matches){logger.debug;thrownewBadCredentialsException;}}/***获取用户。**@paramusername.*@paramauthentication.*@return*@throwsAuthenticationException。*/@OverrideprotectedUserDetailsretrieveUserthrowsAuthenticationException{UserDetailsloadedUser=userDetailsService.loadUserByUsername;如果{thrownewInternalAuthenticationServiceException;}returnloadedUser;}/**授权持久性。*/@OverrideprotectedAuthenticationcreateSuccessAuthentication{returnsuper.createSuccessAuthentication;}AuthenticationFilter的默认实现是UsernamePasswordAuthenticationFilter。您可以自定义它,并在执行之前或之后将其添加到默认过滤器中。它主要用于授权持久化。它可以从请求上下文中获取用户、密码和其他信息,然后判断其是否符合规则,最后通过身份验证方法对其进行授权。

AuthenticationProvider

  • 默认实现:DaoAuthenticationProvider

授权方式提供者,判断授权有效性,用户有效性,在判断用户是否有效性,它依赖于UserDetailsService实例,开发人员可以自定义UserDetailsService的实现。

  1. additionalAuthenticationChecks方法校验密码有效性
  2. retrieveUser方法根据用户名获取用户
  3. createSuccessAuthentication完成授权持久化
@Component
@Slf4j
public class LindAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

  @Autowired
  UserDetailsService userDetailsService;

  @Autowired
  private PasswordEncoder passwordEncoder;

  /**
   * 校验密码有效性.
   *
   * @param userDetails    .
   * @param authentication .
   * @throws AuthenticationException .
   */
  @Override
  protected void additionalAuthenticationChecks(
      UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)
      throws AuthenticationException {
    if (authentication.getCredentials() == null) {
      logger.debug("Authentication failed: no credentials provided");

      throw new BadCredentialsException(messages.getMessage(
          "AbstractUserDetailsAuthenticationProvider.badCredentials",
          "Bad credentials"));
    }

    String presentedPassword = authentication.getCredentials().toString();

    if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
      logger.debug("Authentication failed: password does not match stored value");

      throw new BadCredentialsException(messages.getMessage(
          "AbstractUserDetailsAuthenticationProvider.badCredentials",
          "Bad credentials"));
    }
  }

  /**
   * 获取用户.
   *
   * @param username       .
   * @param authentication .
   * @return
   * @throws AuthenticationException .
   */
  @Override
  protected UserDetails retrieveUser(
      String username, UsernamePasswordAuthenticationToken authentication)
      throws AuthenticationException {
    UserDetails loadedUser = userDetailsService.loadUserByUsername(username);
    if (loadedUser == null) {
      throw new InternalAuthenticationServiceException(
          "UserDetailsService returned null, which is an interface contract violation");
    }
    return loadedUser;
  }
}
 /**
   * 授权持久化.
   */
  @Override
  protected Authentication createSuccessAuthentication(Object principal,
                                                       Authentication authentication, UserDetails user) {
    return super.createSuccessAuthentication(principal, authentication, user);
  }

AuthenticationFilter

  • 默认实现:UsernamePasswordAuthenticationFilter

授权过滤器,你可以自定义它,并把它添加到默认过滤器前或者后去执行,主要用来到授权的持久化,它可以从请求上下文中获取你的user,password等信息,然后去判断它是否符合规则,最后通过authenticate方法去授权。默认的UsernamePasswordAuthenticationFilter过滤器,主要判断请求方式是否为post,并且对username和password进行了默认值的处理,总之,在这个过滤器里不会涉及到具体业务。

public class LindUserNameAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

  public LindUserNameAuthenticationFilter() {
    super(new AntPathRequestMatcher("/login", "GET"));
  }

  @Override
  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
    String username = request.getParameter("username");
    String password = request.getParameter("password");

    if (username == null) {
      throw new InternalAuthenticationServiceException("Failed to get the username");
    }

    if (password == null) {
      throw new InternalAuthenticationServiceException("Failed to get the password");
    }

    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
        username, password);
    authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    return this.getAuthenticationManager().authenticate(authRequest);
  }
}

UserDetialsService

这是一个接口,有默认的实现方式,一般的,我们需要根据业务去重新实现它,比如从你的用户表获取当前授权的用户信息,你需要在UserDetialsService实现类里对用户表进行读取操作;它一般会在AuthenticationProvider里的retrieveUser方法中被使用,这就像面向对象里的模板方法模式一样,springSecurity把检验的步骤设计好了,咱们开发只要根据规则去实现具体细节就好。

@Component
public class MyUserDetailService implements UserDetailsService {
  @Autowired
  private PasswordEncoder passwordEncoder;

  @Override
  public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
    /*
      设置用户和角色需要注意:
      1. commaSeparatedStringToAuthorityList放入角色时需要加前缀ROLE_,而在controller使用时不需要加ROLE_前缀
      2. 放入的是权限时,不能加ROLE_前缀,hasAuthority与放入的权限名称对应即可
    */
    List<UserDetails> userDetailsList = new ArrayList<>();
    userDetailsList.add(User.builder()
        .username("admin")
        .password(passwordEncoder.encode("123"))
        .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("read,ROLE_ADMIN")).build());
    userDetailsList.add(User.builder()
        .username("user")
        .password(passwordEncoder.encode("123"))
        .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("read,ROLE_USER"))
        .build());

    //获取用户
    return userDetailsList.stream()
        .filter(o -> o.getUsername().equals(name))
        .findFirst()
        .orElse(null);

  }
}

在WebSecurityConfig里开启它

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  @Autowired
  LindAuthenticationSuccessHandler lindAuthenticationSuccessHandler;

  @Autowired
  LindAuthenticationFailHandler lindAuthenticationFailHandler;
  @Autowired
  LindAuthenticationProvider lindAuthenticationProvider;

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
        .antMatchers("/", "/index").permitAll()
        .antMatchers("/admin/**").hasRole("ADMIN")//按路由授权
        .anyRequest().authenticated()
        .and()
        .formLogin()
        .loginPage("/login")
        .defaultSuccessUrl("/hello")//默认登录成功后跳转的页面
        .successHandler(lindAuthenticationSuccessHandler)
        .failureHandler(lindAuthenticationFailHandler)
        .permitAll()
        .and()
        .addFilterAt(lindAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class).authorizeRequests().and()
        .logout()
        .permitAll();
  }

  /**
   * 自定义的Filter.
   */
  @Bean
  LindUserNameAuthenticationFilter lindAuthenticationFilter() {
    LindUserNameAuthenticationFilter phoneAuthenticationFilter = new LindUserNameAuthenticationFilter();
    ProviderManager providerManager =
        new ProviderManager(Collections.singletonList(lindAuthenticationProvider));
    phoneAuthenticationFilter.setAuthenticationManager(providerManager);
    phoneAuthenticationFilter.setAuthenticationSuccessHandler(lindAuthenticationSuccessHandler);
    phoneAuthenticationFilter.setAuthenticationFailureHandler(lindAuthenticationFailHandler);
    return phoneAuthenticationFilter;
  }

  /**
   * 密码生成策略.
   *
   * @return
   */
  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }
}

认证时执行的顺序

  1. LindUserNameAuthenticationFilter
  2. LindAuthenticationProvider.retrieveUser
  3. LindAuthenticationProvider.additionalAuthenticationChecks
  4. UserDetailsService
  5. Authentication

springSecurity源码:https://github.com/spring-projects/spring-security

免责声明:文章转载自《SpringSecurity自定义AuthenticationProvider和AuthenticationFilter》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Unity开发-你必须知道的优化建议【C#】C#委托学习下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

OHC Java堆外缓存详解与应用

缓存框架OHC基于Java语言实现,并由其他Java程序以类库的形式调用。它是以独立模式运行的堆外缓存。特定于JVM应用程序,它可以分为堆内缓存和堆外缓存。OHC的全名是堆外缓存或堆外缓存。它是一个基于Java的堆外缓存框架键值。...

vant-picker二次封装

痛点在项目经常会遇到这样的设计,下拉选择框,在vant中没有提供直接的select组件,但是可以使用Field、Popup和Picker这三个组件组合来完成。this.show;}},watch:{selectValue:function{this.result=newVal;},result{this.$emit;}}};效果链接:https://www....

IDEA 运行键是灰色

版权声明:本文为博主原创文章,遵循CC4.0BY-SA版权协议。转载请附上原始来源链接和本声明。本文链接:https://blog.csdn.net/Butterfly_resting/article/details/89388149原因是我们的新项目没有选择源目录,如图所示:解决方案:IDEA提供了选择源目录的快速设置。右键单击src并选择MarkDire...

mysql修改字段防止锁表

步骤1:修改大表、addcolumn或dropcolumn的字段,操作完成后将锁定该表。此时,查询ok、insert和update将等待锁定。...

GERBER文件

GERBER文件GERBER文件是一种国际标准的光绘格式文件,它包含RS-274-D和RS-274-X两种格式,其中RS-274-D称为基本GERBER格式,并要同时附带D码文件才能完整描述一张图形;RS-274-X称为扩展GERBER格式,它本身包含有D码信息。或GERBER描述是防焊层,并且描述之图形主要是防焊部分。若您自己将PCB文件转换成GERBER...

uniapp打包h5 出现'连接服务器超时,点击屏幕重试'的页面

跟踪以首先找出原因全局组件AsyncErrorNew在中注册。js文件可以自定义。我很快就过去了,所以我添加了一个空白页面,然后在清单中介绍了组件。json文件...