本系列開坑編寫的動機是爲了在中文圈爲Spring Security的推廣、使用舔磚加瓦。由於Spring Security幾乎對全部Spring應用都有存在的意義,因此咱們第一期就把Spring Security做爲咱們教程的第一個話題。同期咱們也製做了每期10分鐘的視頻講解,但願能夠彌補僅僅使用文字和圖片描述一個技術知識點上給讀者帶來的困擾。css
最後,在本系列編寫期間,咱們使用的SpringBoot版本爲1.5;Security版本爲5。如因版本差別致使的沒法正常運行和獲取指望結果的狀況發生,能夠經過評論私信給予咱們反饋。謝謝。html
咱們推薦初學者若是對Maven的pom.xml編寫和spring各類starter依賴不熟悉的狀況下,使用Spring官方提供的項目初始化工具進行生成。start.spring.io/ 咱們的目的是初始化一個基於SpringBoot的Web應用,而且包含了Security的相關組件。因此在工具的Web界面上依賴組件部分,須要依次鍵入並選擇Web、Security和Thymeleaf三個組件依賴。 點擊生成後,瀏覽器便會自動下載一個項目工程壓縮包。 前端
解壓縮代碼以後,經過IDEA導入後便會獲得一個已經包含了Security的基礎SpringBoot應用。 運行DemoApplication.java,在控制檯看到應用啓動完畢後,經過瀏覽器訪問本機的127.0.0.1:8080端口,如看到提示輸入用戶名和密碼的登陸對話框,那麼就表明了Security已經成功的加載到了應用中。 java
而咱們的第二個任務就是配置Security框架使其能夠正確的獲取用戶信息用於登陸檢查。 咱們選擇經過JavaConfig的方式類編寫這個配置,類名咱們取名叫WebSecurityConfig,並鍵入下面的代碼。spring
@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;
}
}
複製代碼
咱們在代碼中主要完成的工做就注入了一個的組件。那麼什麼是
呢?數據庫
UserDetails是咱們接觸的第一個重要概念。在Spring Security的觀點中,UserDetails就比如咱們自行設計系統的用戶、帳戶的概念。他包含了用戶名、密碼和其對應的授予權限。瀏覽器
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
複製代碼
UserDetailsSevice就是當前系統中如何獲取庫存用戶信息的服務。bash
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}}
複製代碼
在這裏就能夠簡單的認爲,在咱們輸入用戶名和密碼以後,框架便會經過UserDetailsService 的實現類去尋找驗證用戶前端輸入的用戶名和密碼是否正確,若是正確則返回UserDetails完成登陸操做。Security模式提供了許多種方式的用戶信息管理服務實現,好比基於數據庫、基於LDAP的。咱們當前使用的是最簡單基於內存的用戶管理實現InMemoryUserDetailsManager。app
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
return manager;
}
複製代碼
若是使用的是Sping Security5,則在password部分個字符串爲
.password("{noop}password")
。不然可能會拋出一個異常。框架
重啓應用後,從新訪問http://127.0.0.1:8080/的測試應用,在輸入用戶名和密碼後,則會提示目標訪問的頁面不存在的404錯誤。這起碼證實登陸邏輯已經完成了,稍後簡單編寫一個簡單的控制器和頁面模板就能夠完成第二個任務。 控制器代碼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頁面。
剛剛一個任務咱們完成了Spring Security兩個主要關注點之一的鑑權功能,如今咱們就開始實現一個最簡單的訪問控制邏輯。 首先,咱們先對當前任務的目標作一個簡單的設計: 咱們將在MainController編寫兩個路徑頁面,分別是不須要訪問控制的 ""路徑和須要登陸控制的"\user"路徑。
首先咱們根據設計將MainContoller的代碼補全,添加新的url和對應的視圖模板。user.html對先直接複製index.html,只是把內容改爲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是繼承框架提供的,其中有過一個重要的配置方案咱們關注下框架提供的默認值實現:
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的配置主要有三點:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// inde.html對應的url容許所任人訪問
.antMatchers("/").permitAll()
// user.html對應的url,則須要用戶有USER的角色才能夠訪問
.antMatchers("/user").hasRole("USER")
.and()
.formLogin();
}
複製代碼
咱們使用了hasRole()基於角色的驗證條件,讓我再回顧下,以前咱們在用戶鑑權部分,添加的用戶記錄的代碼是怎麼樣的?咱們添加的user用戶其也包含了一個USER的角色,而在訪問控制的時候便會檢查這一角色受權信息是否匹配。 若是使用的是Spring Security 4 如下的版本則可以使用
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
複製代碼
如使用的是5以上的版本,由於Spring Security中經過配置PasswordEncoder才能進行正確的密碼加密操做User.withDefaultPasswordEncoder()
,不然可能框架會報錯。
manager.createUser(
User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build());
複製代碼
User.withDefaultPasswordEncoder() 是一個僅僅只能使用在示例應用的方法,由於Spring Security 5中對組件的生成機制進行了調整,使用以前的代碼生成用戶信息會致使沒法判斷使用哪一個PasswordEncoder。具體的細節在以後單獨開一個專題進行說明。 感謝朝歌問弦反饋的問題。
重啓應用,首先輸入/路徑,應用沒有提示咱們任何登陸對話框,咱們就看到了Hello Spring的標題。
接着咱們再鍵入\user路徑,由於訪問控制檢查到咱們沒有完成登陸操做,則將咱們重定向到login頁面完成登陸作操 最後,當咱們完成用戶名和輸入以後,由於用戶角色與指望配置的一致,咱們得以訪問目標/user頁面。
咱們在本期中對SpringSecurity最重要的兩個功能:用戶鑑權和訪問控制作了最簡單的實現和配置。 在下一期,咱們將對用戶鑑權部分的具體流程展開講解。讓咱們下期再見。