受權方式提供者,判斷受權有效性,用戶有效性,在判斷用戶是否有效性,它依賴於UserDetailsService實例,開發人員能夠自定義UserDetailsService的實現。git
@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); }
受權過濾器,你能夠自定義它,並把它添加到默認過濾器前或者後去執行,主要用來到受權的持久化,它能夠從請求上下文中獲取你的user,password等信息,而後去判斷它是否符合規則,最後經過authenticate方法去受權。默認的UsernamePasswordAuthenticationFilter
過濾器,主要判斷請求方式是否爲post,而且對username和password進行了默認值的處理,總之,在這個過濾器裏不會涉及到具體業務。github
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實現類裏對用戶表進行讀取操做;它通常會在AuthenticationProvider裏的retrieveUser方法中被使用,這就像面向對象裏的模板方法模式同樣,springSecurity把檢驗的步驟設計好了,我們開發只要根據規則去實現具體細節就好。web
@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); } }
@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(); } }
springSecurity源碼:https://github.com/spring-projects/spring-securityspring