原創(chuàng)性聲明:本文完全為筆者原創(chuàng),請尊重筆者勞動力柱蟀。轉載務必注明原文地址。
在之前的Spring Security(2)——探索篇(源碼分析)中蚜厉,基于官方Demo摸索了一把基于內存認證的源碼长已。這篇嘗試基于數據庫做認證。
基于數據庫認證昼牛,需要先做一些準備:
1.創(chuàng)建
User
對象和UserRepository
接口
2.創(chuàng)建users
表和初始賬號數據
這里就只截個表和文件結構了:
注意:
User
類必須實現(xiàn)UserDetails
接口术瓮。并實現(xiàn)其中的幾個方法。因為我們的users
表只有id贰健、username和password胞四,因此,從UserDetails
中繼承而來的isAccountNonExpired()
伶椿、isAccountNonLocked()
辜伟、isCredentialsNonExpired()
和isEnabled()
直接返回true氓侧,即可。按理說應該映射對應的數據庫屬性导狡。這里為了方便约巷。
思路整理
回到SecurityConfig.java
,再簡單快速理一下思路(Spring security是如何將自定義的user對象存放旱捧,然后在登錄認證的時候與它進行認證):
目前在
configureGlobal
方法里独郎,spring security通過AuthenticationManagerBuilder
的inMemoryAuthentication()
方法實例化AbstractDaoAuthenticationConfigurer
,并初始化其屬性userDetailsService
和DaoAuthenticationProvider provider
枚赡,向provider
中注入了新創(chuàng)建的空的userDetailsService
氓癌,但隨即就通過withUser
和password
創(chuàng)建了一個UserDetailsBuilder
,這里頭記錄了用戶信息贫橙,再用initUserDetailsService
將這個用戶信息關聯(lián)到provider
的userDetailsService
中贪婉。至此完成存放的工作。Spring security
通過AuthenticationManager
這個全局認證管理器在用戶登錄認證時的過濾鏈中觸發(fā)料皇,但認證委托給繼承類ProviderManager
谓松,而真正執(zhí)行認證的是這個類下面的一個屬性List<AuthenticationProvider> providers
, 我們又看到了這個provider
,進一步發(fā)現(xiàn)践剂,AuthenticationProvider
就是DaoAuthenticationProvider
的頂層父級接口類优质,因此在用List<AuthenticationProvider> providers
進行認證時匕争,就是在用DaoAuthenticationProvider provider
拍皮,因此就可以獲取到里面設定的用戶信息了。
當然具體的代碼比較復雜,我看了很多遍都沒有完全理清其中具體的細節(jié)愧驱。但宏觀上大致是這么個思路。
那么比較明顯了,AuthenticationManagerBuilder
這個類就是給我們用各種不同的方式注入用戶信息,以及指定Spring security
用何種方式去認證這個信息(內存、數據庫等等)。我發(fā)現(xiàn)在這個類里除了inMemoryAuthentication()
啃匿,還有幾個方法:
1.
jdbcAuthentication()
:添加jdbc認證裆悄,可以繼續(xù)調用方法dataSource
指定數據源崖技。
2.ldapAuthentication()
:添加LDAP認證。
3.parentAuthenticationManager(AuthenticationManager authenticationManager)
:如果當前AuthenticationManager
無法認證钟哥,將提供給父級認證。
4.userDetailsService(T userDetailsService)
:根據傳入的自定義UserDetailsService添加身份驗證瞎访,自定義UserDetailsService
就是實現(xiàn)spring security
下org.springframework.security.core.userdetails
包下的UserDetailsService
接口腻贰。
那么自定義身份認證,應當使用userDetailsService()
方法扒秸,先實現(xiàn)UserDetailsService
播演。
創(chuàng)建CustomizeUserDetailsService
實現(xiàn)UserDetailsService
:
@Component("userDetailsService")
public class CustomizeUserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
@Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String lowerUsername = username.toLowerCase();
Optional<User> userFromDatabase = userRepository.findOneByUsername(lowerUsername);
return userFromDatabase.map(user -> user).orElseThrow(() -> new UsernameNotFoundException(
"User " + lowerUsername + " was not found in the " + "database"));
}
}
修改SecurityConfig
@EnableWebSecurity // 1
public class SecurityConfig extends WebSecurityConfigurerAdapter { // 2
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() // 3
.antMatchers("/index", "/css/**").permitAll() //4
.antMatchers("/user/**").authenticated()//.hasRole("USER") //5
.and() // 6
.formLogin()
.loginPage("/login")
.failureUrl("/login-error").permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { // 7
auth
// .inMemoryAuthentication() //8
// .withUser("user").password("password").roles("USER"); // 9
.userDetailsService(userDetailsService);
}
}
修改了兩個地方冀瓦,一個是將inMemoryAuthentication
配置注釋掉了,更改為userDetailsService
, 另一個是將 //5
處的hasRole("USER")
注釋了(因為沒有創(chuàng)建角色表和用戶角色關聯(lián)表写烤,在User
類中getAuthorities()
,只返回了一個空的Set<GrantedAuthority> authorities
)翼闽。
清理項目并重新啟動
輸入在數據庫中預輸好的admin和1234,并登陸洲炊,登陸成功:
無論是采用內存認證還是jdbc認證(后面再記錄一篇)或是自定義認證感局,認證機制都是一樣的。下篇將創(chuàng)建角色表和用戶角色關聯(lián)表暂衡,為不同用戶設置角色询微,從而引入Spring security
的另一個重點:授權
。