詳解spring security 配置多個(gè)AuthenticationProvider
前言
發(fā)現(xiàn)很少關(guān)于spring security的文章,基本都是入門級(jí)的,配個(gè)UserServiceDetails或者配個(gè)路由控制就完事了,而且很多還是xml配置,國(guó)內(nèi)通病...so,本文里的配置都是java配置,不涉及xml配置,事實(shí)上我也不會(huì)xml配置
spring security的大體介紹
spring security本身如果只是說(shuō)配置,還是很簡(jiǎn)單易懂的(我也不知道網(wǎng)上說(shuō)spring security難,難在哪里),簡(jiǎn)單不需要特別的功能,一個(gè)WebSecurityConfigurerAdapter的實(shí)現(xiàn),然后實(shí)現(xiàn)UserServiceDetails就是簡(jiǎn)單的數(shù)據(jù)庫(kù)驗(yàn)證了,這個(gè)我就不說(shuō)了。
spring security大體上是由一堆Filter(所以才能在spring mvc前攔截請(qǐng)求)實(shí)現(xiàn)的,F(xiàn)ilter有幾個(gè),登出Filter(LogoutFilter),用戶名密碼驗(yàn)證Filter(UsernamePasswordAuthenticationFilter)之類的,F(xiàn)ilter再交由其他組件完成細(xì)分的功能,例如最常用的UsernamePasswordAuthenticationFilter會(huì)持有一個(gè)AuthenticationManager引用,AuthenticationManager顧名思義,驗(yàn)證管理器,負(fù)責(zé)驗(yàn)證的,但AuthenticationManager本身并不做具體的驗(yàn)證工作,AuthenticationManager持有一個(gè)AuthenticationProvider集合,AuthenticationProvider才是做驗(yàn)證工作的組件,AuthenticationManager和AuthenticationProvider的工作機(jī)制可以大概看一下這兩個(gè)的java doc,然后成功失敗都有相對(duì)應(yīng)該Handler 。大體的spring security的驗(yàn)證工作流程就是這樣了。
開始配置多AuthenticationProvider
首先,寫一個(gè)內(nèi)存認(rèn)證的AuthenticationProvider,這里我簡(jiǎn)單地寫一個(gè)只有root帳號(hào)的AuthenticationProvider
package com.scau.equipment.config.common.security.provider;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**
* Created by Administrator on 2017-05-10.
*/
@Component
public class InMemoryAuthenticationProvider implements AuthenticationProvider {
private final String adminName = "root";
private final String adminPassword = "root";
//根用戶擁有全部的權(quán)限
private final List<GrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority("CAN_SEARCH"),
new SimpleGrantedAuthority("CAN_SEARCH"),
new SimpleGrantedAuthority("CAN_EXPORT"),
new SimpleGrantedAuthority("CAN_IMPORT"),
new SimpleGrantedAuthority("CAN_BORROW"),
new SimpleGrantedAuthority("CAN_RETURN"),
new SimpleGrantedAuthority("CAN_REPAIR"),
new SimpleGrantedAuthority("CAN_DISCARD"),
new SimpleGrantedAuthority("CAN_EMPOWERMENT"),
new SimpleGrantedAuthority("CAN_BREED"));
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if(isMatch(authentication)){
User user = new User(authentication.getName(),authentication.getCredentials().toString(),authorities);
return new UsernamePasswordAuthenticationToken(user,authentication.getCredentials(),authorities);
}
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
private boolean isMatch(Authentication authentication){
if(authentication.getName().equals(adminName)&&authentication.getCredentials().equals(adminPassword))
return true;
else
return false;
}
}
support方法檢查authentication的類型是不是這個(gè)AuthenticationProvider支持的,這里我簡(jiǎn)單地返回true,就是所有都支持,這里所說(shuō)的authentication為什么會(huì)有多個(gè)類型,是因?yàn)槎鄠€(gè)AuthenticationProvider可以返回不同的Authentication。
public Authentication authenticate(Authentication authentication) throws AuthenticationException 方法就是驗(yàn)證過(guò)程。
如果AuthenticationProvider返回了null,AuthenticationManager會(huì)交給下一個(gè)支持authentication類型的AuthenticationProvider處理。
另外需要一個(gè)數(shù)據(jù)庫(kù)認(rèn)證的AuthenticationProvider,我們可以直接用spring security提供的DaoAuthenticationProvider,設(shè)置一下UserServiceDetails和PasswordEncoder就可以了
@Bean
DaoAuthenticationProvider daoAuthenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
daoAuthenticationProvider.setUserDetailsService(userServiceDetails);
return daoAuthenticationProvider;
}
最后在WebSecurityConfigurerAdapter里配置一個(gè)含有以上兩個(gè)AuthenticationProvider的AuthenticationManager,依然重用spring security提供的ProviderManager
package com.scau.equipment.config.common.security;
import com.scau.equipment.config.common.security.handler.AjaxLoginFailureHandler;
import com.scau.equipment.config.common.security.handler.AjaxLoginSuccessHandler;
import com.scau.equipment.config.common.security.provider.InMemoryAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.UserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.Arrays;
import java.util.List;
/**
* Created by Administrator on 2017/2/17.
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService userServiceDetails;
@Autowired
InMemoryAuthenticationProvider inMemoryAuthenticationProvider;
@Bean
DaoAuthenticationProvider daoAuthenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
daoAuthenticationProvider.setUserDetailsService(userServiceDetails);
return daoAuthenticationProvider;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.rememberMe().alwaysRemember(true).tokenValiditySeconds(86400).and()
.authorizeRequests()
.antMatchers("/","/*swagger*/**", "/v2/api-docs").permitAll()
.anyRequest().authenticated().and()
.formLogin()
.loginPage("/")
.loginProcessingUrl("/login")
.successHandler(new AjaxLoginSuccessHandler())
.failureHandler(new AjaxLoginFailureHandler()).and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/");
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/public/**", "/webjars/**", "/v2/**", "/swagger**");
}
@Override
protected AuthenticationManager authenticationManager() throws Exception {
ProviderManager authenticationManager = new ProviderManager(Arrays.asList(inMemoryAuthenticationProvider,daoAuthenticationProvider()));
//不擦除認(rèn)證密碼,擦除會(huì)導(dǎo)致TokenBasedRememberMeServices因?yàn)檎也坏紺redentials再調(diào)用UserDetailsService而拋出UsernameNotFoundException
authenticationManager.setEraseCredentialsAfterAuthentication(false);
return authenticationManager;
}
/**
* 這里需要提供UserDetailsService的原因是RememberMeServices需要用到
* @return
*/
@Override
protected UserDetailsService userDetailsService() {
return userServiceDetails;
}
}
基本上都是重用了原有的類,很多都是默認(rèn)使用的,只不過(guò)為了修改下行為而重新配置。其實(shí)如果偷懶,直接用一個(gè)UserDetailsService,在里面做各種認(rèn)證也是可以的~不過(guò)這樣就沒(méi)意思了
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot Session共享實(shí)現(xiàn)圖解
這篇文章主要介紹了SpringBoot Session共享實(shí)現(xiàn)圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01
Java并發(fā)容器之ConcurrentLinkedQueue詳解
這篇文章主要介紹了Java并發(fā)容器之ConcurrentLinkedQueue詳解,加鎖隊(duì)列的實(shí)現(xiàn)較為簡(jiǎn)單,這里就略過(guò),我們來(lái)重點(diǎn)來(lái)解讀一下非阻塞隊(duì)列,2023-12-12
從點(diǎn)到面, 下面我們來(lái)看下非阻塞隊(duì)列經(jīng)典實(shí)現(xiàn)類ConcurrentLinkedQueue,需要的朋友可以參考下
java中char類型轉(zhuǎn)換成int類型的2種方法
這篇文章主要給大家介紹了關(guān)于java中char類型轉(zhuǎn)換成int類型的2種方法,因?yàn)閖ava是一門強(qiáng)類型語(yǔ)言,所以在數(shù)據(jù)運(yùn)算中會(huì)存在類型轉(zhuǎn)換,需要的朋友可以參考下2023-07-07
springboot mybatis調(diào)用多個(gè)數(shù)據(jù)源引發(fā)的錯(cuò)誤問(wèn)題
這篇文章主要介紹了springboot mybatis調(diào)用多個(gè)數(shù)據(jù)源引發(fā)的錯(cuò)誤問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
Spring Boot Filter 過(guò)濾器的使用方式
這篇文章主要介紹了Spring Boot Filter 過(guò)濾器的使用方式,文章通過(guò)圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
寧可用Lombok也不把成員設(shè)置為public原理解析
這篇文章主要為大家介紹了寧可用Lombok也不把成員設(shè)置為public原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
解決idea每次新建項(xiàng)目都需要重新指定maven目錄
這篇文章主要介紹了解決idea每次新建項(xiàng)目都需要配置maven,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
淺談springMVC攔截器和過(guò)濾器總結(jié)
本篇文章主要介紹了springMVC攔截器和過(guò)濾器總結(jié),可以用來(lái)對(duì)訪問(wèn)的url進(jìn)行攔截處理,有興趣的可以了解一下。2017-01-01

