Spring Security主要做兩件事咐蚯,一件是認(rèn)證,一件是授權(quán)。
1.Spring Security初體驗(yàn)
Spring Security如何使用煞抬,先在你的項(xiàng)目pom.xml文件中聲明依賴狼钮。
<dependency>
<!-- 由于我使用的spring boot所以我是引入spring-boot-starter-security而且我使用了spring io所以不需要填寫依賴的版本號(hào) -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
然后創(chuàng)建一個(gè)類并繼承WebSecurityConfigurerAdapter這個(gè)方法碳柱,并在之類中重寫configure的3個(gè)方法,其中3個(gè)方法中參數(shù)包括為HttpSecurity(HTTP請(qǐng)求安全處理)熬芜,AuthenticationManagerBuilder(身份驗(yàn)證管理生成器)和WebSecurity(WEB安全)莲镣。
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth){
super.configure(auth);
}
@Override
protected void configure(HttpSecurity http){
super.configure(http);
}
@Override
protected void configure(WebSecurity web){
super.configure(web);
}
}
接下來我們先看看protected void configure(HttpSecurity http)這個(gè)方法提供了一個(gè)默認(rèn)的配置。
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
http.authorizeRequests()其中這里的意思是指通過authorizeRequests()方法來開始請(qǐng)求權(quán)限配置涎拉。
而接著的.anyRequest().authenticated()是對(duì)http所有的請(qǐng)求必須通過授權(quán)認(rèn)證才可以訪問瑞侮。
而and()是返回一個(gè)securityBuilder對(duì)象的圆,formLogin()和httpBasic()是授權(quán)的兩種方式。
當(dāng)然這些界面都是spring security原生的界面区岗,我們也可以自定義我們的formLogin頁面略板!
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
//指定登錄頁的路徑
.loginPage("/login")
//必須允許所有用戶訪問我們的登錄頁(例如未驗(yàn)證的用戶,否則驗(yàn)證流程就會(huì)進(jìn)入死循環(huán))
//這個(gè)formLogin().permitAll()方法允許所有用戶基于表單登錄訪問/login這個(gè)page慈缔。
.permitAll();
}
提示一下叮称,這個(gè)自定義表單登錄的自定義頁面中的登錄名參數(shù)必須被命名為username
密碼參數(shù)必須被命名為password。
而接下來當(dāng)我們需要對(duì)某些開放的url藐鹤,給與任何人訪問的時(shí)候瓤檐,我們應(yīng)該如何設(shè)置呢?答案很簡(jiǎn)單我們先看著代碼慢慢深入娱节!
protected void configure(HttpSecurity http) throws Exception {
http
//http.authorizeRequests()方法有多個(gè)子節(jié)點(diǎn)挠蛉,每個(gè)macher按照他們的聲明順序執(zhí)行
.authorizeRequests()
//我們指定任何用戶都可以訪問多個(gè)URL的模式。
//任何用戶都可以訪問以"/resources/","/signup", 或者 "/about"開頭的URL肄满。
.antMatchers("/resources/**", "/signup", "/about").permitAll()
//以 "/admin/" 開頭的URL只能讓擁有 "ROLE_ADMIN"角色的用戶訪問谴古。
//請(qǐng)注意我們使用 hasRole 方法,沒有使用 "ROLE_" 前綴稠歉。
.antMatchers("/admin/**").hasRole("ADMIN")
//任何以"/db/" 開頭的URL需要同時(shí)具有 "ROLE_ADMIN" 和 "ROLE_DBA"權(quán)限的用戶才可以訪問掰担。
//和上面一樣我們的 hasRole 方法也沒有使用 "ROLE_" 前綴。
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
//任何以"/db/" 開頭的URL只需要擁有 "ROLE_ADMIN" 和 "ROLE_DBA"其中一個(gè)權(quán)限的用戶才可以訪問怒炸。
//和上面一樣我們的 hasRole 方法也沒有使用 "ROLE_" 前綴带饱。
.antMatchers("/db/**").hasAnyRole("ADMIN", "DBA")
//尚未匹配的任何URL都要求用戶進(jìn)行身份驗(yàn)證
.anyRequest().authenticated()
.and()
// ...
.formLogin();
}
我們可以在authorizeRequests() 后定義多個(gè)antMatchers()配置器來控制不同的url接受不同權(quán)限的用戶訪問,而其中permitAll() 方法是運(yùn)行所有權(quán)限用戶包含匿名用戶訪問阅羹。
而hasRole("權(quán)限")則是允許這個(gè)url給與參數(shù)中相等的權(quán)限訪問勺疼。
access("hasRole('權(quán)限') and hasRole('權(quán)限')") 是指允許訪問這個(gè)url必須同時(shí)擁有參數(shù)中多個(gè)身份權(quán)限才可以訪問。
hasAnyRole("ADMIN", "DBA")是指允許訪問這個(gè)url必須同時(shí)擁有參數(shù)中多個(gè)身份權(quán)限中的一個(gè)就可以訪問該url捏鱼。
2.Spring Security定制登錄退出行為
我們接下來就簡(jiǎn)單的定制一下登錄登出行為导梆!
protected void configure(HttpSecurity http) throws Exception {
http
//通過formlogin方法登錄轨淌,并設(shè)置登錄url為/api/user/login
.formLogin().loginPage("/api/user/login")
//指定登錄成功后跳轉(zhuǎn)到/index頁面
.defaultSuccessUrl("/index")
//指定登錄失敗后跳轉(zhuǎn)到/login?error頁面
.failureUrl("/login?error")
.permitAll()
.and()
//開啟cookie儲(chǔ)存用戶信息,并設(shè)置有效期為14天问潭,指定cookie中的密鑰
.rememberMe().tokenValiditySeconds(1209600).key("mykey")
.and()
.logout()
//指定登出的url
.logoutUrl("/api/user/logout")
//指定登場(chǎng)成功之后跳轉(zhuǎn)的url
.logoutSuccessUrl("/index")
.permitAll();
}
3.Spring Security定制自定義用戶認(rèn)證
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
//重寫了configure參數(shù)為AuthenticationManagerBuilder的方法
protected void configure(AuthenticationManagerBuilder auth){
//并根據(jù)傳入的AuthenticationManagerBuilder中的userDetailsService方法來接收我們自定義的認(rèn)證方法。
//且該方法必須要實(shí)現(xiàn)UserDetailsService這個(gè)接口婚被。
auth.userDetailsService(new myUserDetailsService())
//密碼使用BCryptPasswordEncoder()方法驗(yàn)證狡忙,因?yàn)檫@里使用了BCryptPasswordEncoder()方法驗(yàn)證。所以在注冊(cè)用戶的時(shí)候在接收前臺(tái)明文密碼之后也需要使用BCryptPasswordEncoder().encode(明文密碼)方法加密密碼址芯。
.passwordEncoder(new BCryptPasswordEncoder());;
}
@Override
protected void configure(HttpSecurity http){
super.configure(http);
}
@Override
protected void configure(WebSecurity web){
super.configure(web);
}
}
新建myUserDetailsService方法并實(shí)現(xiàn)UserDetailsService這個(gè)接口
@Component
public class myUserDetailsService implements UserDetailsService {
@Autowired
//由于是演示這里就不再創(chuàng)建service層了灾茁,直接注入U(xiǎn)serRepository窜觉。
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
//查詢賬號(hào)是否存在,是就返回一個(gè)UserDetails的對(duì)象北专,否就拋出異常禀挫!
User user = userRepository.findByName(userName);
if (user == null) {
throw new UsernameNotFoundException("UserName " + userName + " not found");
}
return new SecurityUser(user);
}
}
基本的認(rèn)證邏輯就到這里了,對(duì)于有另外的業(yè)務(wù)需求都可以在自定義的myUserDetailsService中處理完成拓颓!
4.Spring Security定制自定義授權(quán)策略
@EnableGlobalAuthentication
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/admin").permitAll()
//使用自定義授權(quán)策略
.anyRequest().access("@mySecurity.check(authentication,request)");
}
}
新建MySecurity類
@Component("mySecurity")
public class MySecurity(){
//這里應(yīng)該注入用戶和該用戶所擁有的權(quán)限(權(quán)限在登錄成功的時(shí)候已經(jīng)緩存起來语婴,當(dāng)需要訪問該用戶的權(quán)限是,直接從緩存取出J荒馈)砰左,然后驗(yàn)證該請(qǐng)求是否有權(quán)限,有就返回true场航,否則則返回false不允許訪問該Url缠导。
//而且這里還傳入了request,我也可以使用request獲取該次請(qǐng)求的類型。
//根據(jù)restful風(fēng)格我們可以使用它來控制我們的權(quán)限溉痢,例如當(dāng)這個(gè)請(qǐng)求是post請(qǐng)求僻造,證明該請(qǐng)求是向服務(wù)器發(fā)送一個(gè)新建資源請(qǐng)求,我們可以使用request.getMethod()來獲取該請(qǐng)求的方式孩饼,然后在配合角色所允許的權(quán)限路徑進(jìn)行判斷和授權(quán)操作髓削!
public boolean check(Authentication authentication, HttpServletRequest request){
//如果能獲取到Principal對(duì)象不為空證明,授權(quán)已經(jīng)通過
Object principal = authentication.getPrincipal();
if(principal != null && principal instanceof UserDetails){
//獲取請(qǐng)求登錄的url
System.out.println(((UserDetails)principal).getAuthorities()) ;
return true;
}
return false;
}
}