AuthenticationProvider
- 默認實現:DaoAuthenticationProvider
授權方式提供者,判斷授權有效性,用戶有效性,在判斷用戶是否有效性,它依賴於UserDetailsService實例,開發人員可以自定義UserDetailsService的實現。
- additionalAuthenticationChecks方法校驗密碼有效性
- retrieveUser方法根據用戶名獲取用戶
- 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();
}
}
認證時執行的順序
- LindUserNameAuthenticationFilter
- LindAuthenticationProvider.retrieveUser
- LindAuthenticationProvider.additionalAuthenticationChecks
- UserDetailsService
- Authentication
springSecurity源碼:https://github.com/spring-projects/spring-security