Spring Security中進行身份驗證的是AuthenticationManager接口,ProviderManager是它的一個默認實現,但它並不用來處理身份認證,而是委托給配置好的AuthenticationProvider,每個AuthenticationProvider會輪流檢查身份認證。
具體流程可參考Spring Security認證流程
此次記錄在Spring Security 連接數據庫實現認證的基礎上增加。
自定義AuthenticationProvider:
package cn.sivan.provider;
import cn.sivan.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
public class LoginAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserService userService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//用戶名 密碼
String username = authentication.getName();
String password = (String) authentication.getCredentials();
//通過用戶名獲取用戶信息
UserDetails userDetails = userService.loadUserByUsername(username);
if (userDetails == null) {
throw new UsernameNotFoundException("用戶不存在!");
}
if (!passwordEncoder.matches(password, userDetails.getPassword())) {
throw new BadCredentialsException("密碼不正確!");
}
return new UsernamePasswordAuthenticationToken(username, password, userDetails.getAuthorities());
}
/**
* AuthenticationManager 本身不包含認證邏輯,其核心是用來管理所有的 AuthenticationProvider,通過交由合適的 AuthenticationProvider 來實現認證。
* ProviderManager 是AuthenticationProvider的實現類
* AuthenticationManager獲取所有AuthenticationProvider的實現
* 通過該方法判斷是否支持當前方式的認證
* 這里支持驗證UsernamePasswordAuthenticationToken
* @param authentication
* @return
*/
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
SpringSecurity配置:
package cn.sivan.config;
import cn.sivan.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@EnableWebSecurity
public class SpringSecurity extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Autowired
private AuthenticationProvider loginValidateAuthenticationProvider;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//指定userDetailsService
auth.userDetailsService(userService).passwordEncoder(passwordEncoder);
//注冊自定義認證
auth.authenticationProvider(loginValidateAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/favicon.ico").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login/user")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/")
.failureUrl("/login/failure")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/login/user")
.logoutUrl("/logout")
.invalidateHttpSession(true)
.permitAll()
.and()
.csrf()
.disable();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}