SpringSecurity是在攔截器Interceptor前面的。
SpringSecurity走完,如果項目中設定了攔截器,還會走攔截器的。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
添加依賴後,啟動項目,通路任意接口都會跳轉到登入頁
輸入賬号,密碼,程式會先執行到
UsernamePasswordAuthenticationFilter
的attemptAuthentication方法
// UsernamePasswordAuthenticationFilter.class
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response){
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
String username = this.obtainUsername(request); // 從HttpServletRequest中拿到傳來的的使用者名參數
username = username != null ? username : "";
username = username.trim();
String password = this.obtainPassword(request); // 從HttpServletRequest中拿到傳來的的密碼參數
password = password != null ? pasword : "";
// 封裝 Authentication
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
this.setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest); // 試圖認證
}
}
進入到
ProviderManager
這個類當中的authenticate方法。
之後調用
DaoAuthenticationProvider
繼承的其父類
public abstract class AbstractUserDetailsAuthenticationProvider
的authenticate方法。
之後進入到
DaoAuthenticationProvider
這個類中retrieveUser方法。
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) {
this.prepareTimingAttackProtection();
try { // 根據使用者名在系統中查詢使用者資訊
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
} else {
return loadedUser;
}
} catch (Exception var6) {
throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
}
}
之後又進入到
public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService
這個類中的loadUserByUsername方法
public UserDetails loadUserByUsername(String username){
// 根據使用者名在系統中查詢使用者資訊
// InMemory是預設記憶體中存着相應的使用者資訊
UserDetails user = (UserDetails)this.users.get(username.toLowerCase());
if (user == null) {
throw new UsernameNotFoundException(username);
} else {
// 封裝傳回
return new User(user.getUsername(), user.getPassword(), user.isEnabled(),
user.isAccountNonExpired(), user.isCredentialsNonExpired(),
user.isAccountNonLocked(), user.getAuthorities());
}
}
查詢到的話,封裝為UserDetails,又層層傳回至
AbstractUserDetailsAuthenticationProvider
類中的authenticate方法。
之後又進入到
DaoAuthenticationProvider
類中的additionalAuthenticationChecks方法。
// userDetails是在系統中查到的使用者資訊, authentication是登入時填寫的資訊進行封裝
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication){
if (authentication.getCredentials() == null) {
this.logger.debug("Failed to authenticate since no credentials provided");
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
} else {
// 比對密碼
String presentedPassword = authentication.getCredentials().toString();
if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
this.logger.debug("Failed to authenticate since password does not match stored value");
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
}
比對成功之後,又傳回到
AbstractUserDetailsAuthenticationProvider
類中的authenticate方法,調用
DaoAuthenticationProvider
類中的createSuccessAuthentication方法
将從系統中查到的UserDetails資訊(改名為principal),以及authentication(使用者登陸時填的資訊),以及一個MutableUserDetails(将從系統中查到的UserDetails資訊,将其中的密碼進行Bcrpt加密,封裝為MutableUserDetails對象)這三者進行整合,最後封裝資訊,傳回一個UsernamePasswordAuthenticationToken。