本文閱讀約需要10分鐘
本期的配套視頻講解請?jiān)L問https://www.bilibili.com/video/av45427128/
前言
本系列開坑編寫的動機(jī)是為了在中文圈為Spring Security的推廣、使用舔磚加瓦。因?yàn)镾pring Security幾乎對所有Spring應(yīng)用都有存在的意義张肾,所以我們第一期就把Spring Security作為我們教程的第一個話題。同期我們也制作了每期10分鐘的視頻講解,希望可以彌補(bǔ)僅僅使用文字和圖片描述一個技術(shù)知識點(diǎn)上給讀者帶來的困擾炸渡。
最后,在本系列編寫期間丽已,我們使用的SpringBoot版本為1.5蚌堵;Security版本為4。如因版本差異導(dǎo)致的無法正常運(yùn)行和獲取期望結(jié)果的情況發(fā)生沛婴,可以通過評論私信給予我們反饋吼畏。謝謝。
第一期 快速為Spring Boot應(yīng)用添加Spring Security
本期的任務(wù)清單
- 快速初始化一個Spring Boot項(xiàng)目
- 如何添加基于內(nèi)存的用戶鑒權(quán)功能
- 如何添加基于角色的訪問控制邏輯
1嘁灯、快速初始化一個Spring Boot項(xiàng)目
我們推薦初學(xué)者如果對Maven的pom.xml編寫和spring各種starter依賴不熟悉的情況下泻蚊,使用Spring官方提供的項(xiàng)目初始化工具進(jìn)行生成。https://start.spring.io/
我們的目的是初始化一個基于SpringBoot的Web應(yīng)用丑婿,并且包含了Security的相關(guān)組件性雄。所以在工具的Web界面上依賴組件部分,需要依次鍵入并選擇Web羹奉、Security和Thymeleaf三個組件依賴秒旋。
點(diǎn)擊生成后,瀏覽器便會自動下載一個項(xiàng)目工程壓縮包诀拭。
解壓縮代碼之后迁筛,通過IDEA導(dǎo)入后便會得到一個已經(jīng)包含了Security的基礎(chǔ)SpringBoot應(yīng)用。
運(yùn)行DemoApplication.java炫加,在控制臺看到應(yīng)用啟動完畢后瑰煎,通過瀏覽器訪問本機(jī)的127.0.0.1:8080端口,如看到提示輸入用戶名和密碼的登錄對話框俗孝,那么就代表了Security已經(jīng)成功的加載到了應(yīng)用中酒甸。
2、添加基于內(nèi)存的用戶鑒權(quán)功能
鑒權(quán)和訪問控制的區(qū)分
首先赋铝,我們要對Authentication和Authorization兩個詞做一個區(qū)別插勤。在中文語義上我們會把Authentication稱為鑒權(quán),用戶認(rèn)證,通常是一個對根據(jù)某些信息农尖、比如用戶名析恋、密碼、令牌等來辨別用戶的過程盛卡。而Authorization在中文語義上可以理解為授權(quán)助隧、訪問控制。為了將來好區(qū)別我們將來會統(tǒng)一把Authorization稱為訪問控制(Access Control)滑沧,因?yàn)锳uthorization是根據(jù)用戶的角色并村、權(quán)限等信息來判斷目標(biāo)行為、資源是不是可以被對應(yīng)的用戶使用滓技、消費(fèi)哩牍。
簡單的說,鑒權(quán)就是用戶登錄令漂、授權(quán)就是權(quán)限控制膝昆。
而我們的第二個任務(wù)就是配置Security框架使其可以正確的獲取用戶信息用于登錄檢查。
我們選擇通過JavaConfig的方式類編寫這個配置叠必,類名我們?nèi)∶蠾ebSecurityConfig荚孵,并鍵入下面的代碼。
@EnableWebSecurity //配置注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//注入新的UserDetailsServiceBean
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
return manager;
}
}
我們在代碼中主要完成的工作就注入了一個的組件挠唆。那么什么是呢处窥?
UserDetailsService 和 UserDetails
UserDetails是我們接觸的第一個重要概念嘱吗。在Spring Security的觀點(diǎn)中玄组,UserDetails就好比我們自行設(shè)計(jì)系統(tǒng)的用戶、賬戶的概念谒麦。他包含了用戶名俄讹、密碼和其對應(yīng)的授予權(quán)限。
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
UserDetailsSevice就是當(dāng)前系統(tǒng)中如何獲取庫存用戶信息的服務(wù)绕德。
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}}
在這里就可以簡單的認(rèn)為患膛,在我們輸入用戶名和密碼之后,框架便會通過UserDetailsService 的實(shí)現(xiàn)類去尋找驗(yàn)證用戶前端輸入的用戶名和密碼是否正確耻蛇,如果正確則返回UserDetails完成登錄操作踪蹬。Security模式提供了許多種方式的用戶信息管理服務(wù)實(shí)現(xiàn),比如基于數(shù)據(jù)庫臣咖、基于LDAP的跃捣。我們當(dāng)前使用的是最簡單基于內(nèi)存的用戶管理實(shí)現(xiàn)InMemoryUserDetailsManager。
我們通過新建InMemoryUserDetailsManager夺蛇,然后通過createUser方法向其添加了一條用戶記錄疚漆。最終將其注入Spring框架,使其為我們的應(yīng)用在登錄時候可以正確的查找到我們期望的用戶記錄。
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
return manager;
}
重啟應(yīng)用后娶聘,重新訪問http://127.0.0.1:8080/的測試應(yīng)用闻镶,在輸入用戶名和密碼后,則會提示目標(biāo)訪問的頁面不存在的404錯誤丸升。這起碼證明登錄邏輯已經(jīng)完成了铆农,稍后簡單編寫一個簡單的控制器和頁面模板就可以完成第二個任務(wù)。
控制器代碼MainController
@Controller
public class MainController {
@RequestMapping("/")
public String root() {
return "index";
}
頁面模板代碼 index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<title>Hello Spring</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
<h1>Hello Spring</h1>
</body>
</html>
在登陸后便可以看到我們期望的hello頁面狡耻。
3顿涣、 如何添加基于角色的訪問控制邏輯
剛剛一個任務(wù)我們完成了Spring Security兩個主要關(guān)注點(diǎn)之一的鑒權(quán)功能,現(xiàn)在我們就開始實(shí)現(xiàn)一個最簡單的訪問控制邏輯酝豪。
首先涛碑,我們先對當(dāng)前任務(wù)的目標(biāo)做一個簡單的設(shè)計(jì):
我們將在MainController編寫兩個路徑頁面,分別是不需要訪問控制的 ""路徑和需要登錄控制的"\user"路徑孵淘。
編寫控制器
首先我們根據(jù)設(shè)計(jì)將MainContoller的代碼補(bǔ)全蒲障,添加新的url和對應(yīng)的視圖模板。user.html對先直接復(fù)制index.html瘫证,只是把內(nèi)容改成Welcome User就可以了揉阎。
MainController .java
@Controller
public class MainController {
@RequestMapping("/")
public String root() {
return "index";
}
@RequestMapping("/user")
public String userIndex() {
return "user";
}
}
user.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<title>Welcome User</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
<h1>Welcome User</h1>
</body>
</html>
編寫訪問控制配置
我們的配置類WebSecurityConfig是繼承框架提供的,其中有過一個重要的配置方案我們關(guān)注下框架提供的默認(rèn)值實(shí)現(xiàn):
protected void configure(HttpSecurity http) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
這一段代碼主要搞事我們一個HttpSecurity的配置主要有三點(diǎn):
- authorizeRequests()下管理路徑訪問控制背捌;
- formLogin()管理登錄表單配置毙籽;
- httpBasic()是否基于Http的驗(yàn)證配置。
后兩點(diǎn)不是我們的重點(diǎn)毡庆,我們的目標(biāo)是配置路徑的訪問控制坑赡,所以我們需要在我們自己的配置類覆寫這個方法:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// inde.html對應(yīng)的url允許所任人訪問
.antMatchers("/").permitAll()
// user.html對應(yīng)的url,則需要用戶有USER的角色才可以訪問
.antMatchers("/user").hasRole("USER")
.and()
.formLogin();
}
我們使用了hasRole()基于角色的驗(yàn)證條件么抗,讓我再回顧下毅否,之前我們在用戶鑒權(quán)部分,添加的用戶記錄的代碼是怎么樣的?我們添加的user用戶其也包含了一個USER的角色蝇刀,而在訪問控制的時候便會檢查這一角色授權(quán)信息是否匹配螟加。
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
重啟應(yīng)用,首先輸入/路徑吞琐,應(yīng)用沒有提示我們?nèi)魏蔚卿泴υ捒蚶μ剑覀兙涂吹搅薍ello Spring的標(biāo)題。
接著我們再鍵入\user路徑站粟,因?yàn)樵L問控制檢查到我們沒有完成登錄操作黍图,則將我們重定向到login頁面完成登錄做操
最后,當(dāng)我們完成用戶名和輸入之后卒蘸,因?yàn)橛脩艚巧c期望配置的一致雌隅,我們得以訪問目標(biāo)/user頁面翻默。
這樣我們也完成最簡單的訪問控制配置的任務(wù)。
結(jié)尾
我們在本期中對SpringSecurity最重要的兩個功能:用戶鑒權(quán)和訪問控制做了最簡單的實(shí)現(xiàn)和配置恰起。
在下一期修械,我們將對用戶鑒權(quán)部分的具體流程展開講解。讓我們下期再見检盼。