Spring Security自定義認(rèn)證器的實(shí)現(xiàn)代碼
在了解過(guò)Security的認(rèn)證器后,如果想自定義登陸,只要實(shí)現(xiàn)AuthenticationProvider還有對(duì)應(yīng)的Authentication就可以了
Authentication
首先要?jiǎng)?chuàng)建一個(gè)自定義的Authentication,Security提供了一個(gè)Authentication的子類AbstractAuthenticationToken
我們實(shí)現(xiàn)這個(gè)類可以了,他已經(jīng)實(shí)現(xiàn)了Authentication的一些方法
public class NamePassAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 520L;
private final Object principal;
private Object credentials;
//提供第一次進(jìn)來(lái)的構(gòu)造方法
public NamePassAuthenticationToken(Object principal, Object credentials) {
super((Collection)null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
//提供填充Authentication的構(gòu)造方法
public NamePassAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return this.credentials;
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
} else {
super.setAuthenticated(false);
}
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
this.credentials = null;
}
}這個(gè)類關(guān)鍵就是一個(gè)是認(rèn)證的,一個(gè)沒(méi)認(rèn)證的的構(gòu)造器
AuthenticationProvider
接著是AuthenticationProvider,需要實(shí)現(xiàn)他的authenticate方法
@Setter
public class NamePassAuthenticationProvider implements AuthenticationProvider {
private CustomUserDetailsService userDetailsService;
private PasswordEncoder passwordEncoder;
@Override
//具體認(rèn)證邏輯
public Authentication authenticate(Authentication authentication) {
NamePassAuthenticationToken authenticationToken = (NamePassAuthenticationToken) authentication;
String username = (String) authenticationToken.getPrincipal();
String password = (String) authenticationToken.getCredentials();
//讓具體認(rèn)證類去認(rèn)證
UserDetails user = userDetailsService.loadUserByUsername(username);
boolean matches = passwordEncoder.matches(password, user.getPassword());
if (!matches) {
ResMsg.throwException(AuthExceptionGroup.AUTH_ERROR);
}
//填充Authentication
NamePassAuthenticationToken authenticationResult = new NamePassAuthenticationToken(user, password, user.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
}
@Override
//指定具體的Authentication
//根據(jù)你指定的Authentication來(lái)找到具體的Provider
public boolean supports(Class<?> authentication) {
return NamePassAuthenticationToken.class.isAssignableFrom(authentication);
}
}SecurityConfigurerAdapter
接著就是填充配置了
@Component
public class NamePassAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(HttpSecurity http) {
//phonePass provider
NamePassAuthenticationProvider provider = new NamePassAuthenticationProvider();
provider.setUserDetailsService(customUserDetailsService);
provider.setPasswordEncoder(passwordEncoder);
http.authenticationProvider(provider);
}
}接下來(lái)就是導(dǎo)入配置了
通常都會(huì)有一個(gè)實(shí)現(xiàn)了WebSecurityConfigurerAdapter的配置類
把配置類注入進(jìn)來(lái)
@Autowired
private NamePassAuthenticationSecurityConfig namePassAuthenticationSecurityConfig;
protected void configure(HttpSecurity http) throws Exception {
http.apply(namePassAuthenticationSecurityConfig);
}UserDetailsService
UserDetailsService是具體的認(rèn)證實(shí)現(xiàn)類
這個(gè)類就非常熟悉了,只需要實(shí)現(xiàn)他的loadUserByUsername方法,就可以實(shí)現(xiàn)認(rèn)證了
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AuthmsViewAccount account = accountService.getAccount(username);
if(account == null) {
ResMsg.throwException(AUTH_ERROR);
}
if (account.getStatus() != 1) {
ResMsg.throwException(ACCOUNT_HAS_BANED);
}
String spliceStaffInfo = String.format("%d-%s",account.getAccountId(),account.getUsername());
//只要Collection<? extends GrantedAuthority> authorities
//這個(gè)參數(shù)不為空,就表明認(rèn)證通過(guò),所以空集合也可以通過(guò)
return new User(spliceStaffInfo,account.getPassword(), AuthorityUtils.NO_AUTHORITIES);
}把認(rèn)證結(jié)果填充到上下文中
TokenFilter
如果結(jié)合了Token,那么需要從token中識(shí)別該用戶
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String bearerToken = resolveToken(request);
if (bearerToken != null && !"".equals(bearerToken.trim()) && SecurityContextHolder.getContext().getAuthentication() == null) {
//從redis中獲取該用戶
NamePassAuthenticationToken namePassAuthenticationToken = authRedisHelper.get(bearerToken);
if(namePassAuthenticationToken != null) {
//將信息保存到上下文中
SecurityContextHolder.getContext().setAuthentication(namePassAuthenticationToken);
}
}
chain.doFilter(request, response);
}
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
return bearerToken.substring(7);
}
return null;
}public NamePassAuthenticationToken get(String bearerToken){
String spliceStaffInfo = (String)redisRepository.get(formatKey(bearerToken));
if(spliceStaffInfo == null) {
return null;
}
return new NamePassAuthenticationToken(new AuthStaff(spliceStaffInfo),null,AuthorityUtils.NO_AUTHORITIES);
}登錄過(guò)程
在登錄的時(shí)候,就需要用到這個(gè)自定義的認(rèn)證器了
// 通過(guò)用戶名和密碼創(chuàng)建一個(gè) Authentication 認(rèn)證對(duì)象,實(shí)現(xiàn)類為 NamePassAuthenticationToken NamePassAuthenticationToken authenticationToken = new NamePassAuthenticationToken(user.getUsername(), user.getPassword()); //通過(guò) AuthenticationManager(默認(rèn)實(shí)現(xiàn)為ProviderManager)的authenticate方法驗(yàn)證 Authentication 對(duì)象 //AuthenticationManager會(huì)通過(guò)你傳入的authenticationToken來(lái)找到具體的Provider Authentication authentication = authenticationManager.authenticate(authenticationToken); //填充用戶信息到secrity中的user里 User principal = (User) authentication.getPrincipal(); //獲取認(rèn)證后的信息 NamePassAuthenticationToken namePassAuthenticationToken = new NamePassAuthenticationToken(new AuthStaff(principal.getUsername()), null, authentication.getAuthorities()); // 生成token String bearerToken = IdUtil.fastSimpleUUID(); // 加載到reids authRedisHelper.set(bearerToken, namePassAuthenticationToken);
這樣就實(shí)現(xiàn)了自定義的認(rèn)證器了
到此這篇關(guān)于Spring Security自定義認(rèn)證器的文章就介紹到這了,更多相關(guān)Spring Security自定義認(rèn)證器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?properties?和?yml?的區(qū)別解析
properties和yml都是Spring?Boot支持的兩種配置文件,它們可以看做Spring?Boot在不同時(shí)期的兩種“產(chǎn)品”,這篇文章主要介紹了Java?properties?和?yml?的區(qū)別,需要的朋友可以參考下2023-02-02
java編程實(shí)現(xiàn)求質(zhì)數(shù)與因式分解代碼分享
這篇文章主要介紹了Java編程實(shí)現(xiàn)求質(zhì)數(shù)與因式分解代碼分享,對(duì)二者的概念作了簡(jiǎn)單介紹(多此一舉,哈哈),都是小學(xué)數(shù)學(xué)老師的任務(wù),然后分享了求解質(zhì)數(shù)和因式分解的Java代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12
mybatisplus報(bào)Invalid bound statement (not found)錯(cuò)誤的解決方法
搭建項(xiàng)目時(shí)使用了mybatisplus,項(xiàng)目能夠正常啟動(dòng),但在調(diào)用mapper方法查詢數(shù)據(jù)庫(kù)時(shí)報(bào)Invalid bound statement (not found)錯(cuò)誤。本文給大家分享解決方案,感興趣的朋友跟隨小編一起看看吧2020-08-08
Java里的static在Kotlin里如何實(shí)現(xiàn)
這篇文章主要介紹了Java里的static在Kotlin里如何實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
安裝多個(gè)版本JDK后使用時(shí)的切換方法總結(jié)
我們平時(shí)在window上做開(kāi)發(fā)的時(shí)候,可能需要同時(shí)開(kāi)發(fā)兩個(gè)甚至多個(gè)項(xiàng)目,有時(shí)不同的項(xiàng)目對(duì)JDK的版本要求有區(qū)別,下面這篇文章主要給大家介紹了安裝多個(gè)版本JDK后使用的切換方法,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-01-01
詳解Java是如何通過(guò)接口來(lái)創(chuàng)建代理并進(jìn)行http請(qǐng)求
今天給大家?guī)?lái)的知識(shí)是關(guān)于Java的,文章圍繞Java是如何通過(guò)接口來(lái)創(chuàng)建代理并進(jìn)行http請(qǐng)求展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06
SpringBoot Controller Post接口單元測(cè)試示例
今天小編就為大家分享一篇關(guān)于SpringBoot Controller Post接口單元測(cè)試示例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12
SpringBoot+URule實(shí)現(xiàn)可視化規(guī)則引擎的方法示例
規(guī)則引擎其實(shí)是一種組件,它可以嵌入到程序當(dāng)中,將程序復(fù)雜的判斷規(guī)則從業(yè)務(wù)代碼中剝離出來(lái),使得程序只需要關(guān)心自己的業(yè)務(wù),而不需要去進(jìn)行復(fù)雜的邏輯判斷,本文給大家介紹了SpringBoot+URule實(shí)現(xiàn)可視化規(guī)則引擎的方法示例,需要的朋友可以參考下2024-12-12
Java經(jīng)典設(shè)計(jì)模式之適配器模式原理與用法詳解
這篇文章主要介紹了Java經(jīng)典設(shè)計(jì)模式之適配器模式,簡(jiǎn)單說(shuō)明了適配器模式的概念、原理,并結(jié)合實(shí)例形式分析了java適配器模式的用法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-08-08
Apache?log4j2-RCE?漏洞復(fù)現(xiàn)及修復(fù)建議(CVE-2021-44228)
Apache?Log4j2是一款Java日志框架,大量應(yīng)用于業(yè)務(wù)系統(tǒng)開(kāi)發(fā)。2021年11月24日,阿里云安全團(tuán)隊(duì)向Apache官方報(bào)告了Apache?Log4j2遠(yuǎn)程代碼執(zhí)行漏洞(CVE-2021-44228),本文給大家介紹Apache?log4j2-RCE?漏洞復(fù)現(xiàn)(CVE-2021-44228)的相關(guān)知識(shí),感興趣的朋友一起看看吧2021-12-12

