Spring Security小教程 Vol 1. 最簡單的應用

噗噗虎和阿基拉的廢柴大叔小課堂
本文閱讀約須要10分鐘 本期的配套視頻講解請訪問https://www.bilibili.com/video/av45427128/

前言

本系列開坑編寫的動機是爲了在中文圈爲Spring Security的推廣、使用舔磚加瓦。由於Spring Security幾乎對全部Spring應用都有存在的意義,因此咱們第一期就把Spring Security做爲咱們教程的第一個話題。同期咱們也製做了每期10分鐘的視頻講解,但願能夠彌補僅僅使用文字和圖片描述一個技術知識點上給讀者帶來的困擾。css

最後,在本系列編寫期間,咱們使用的SpringBoot版本爲1.5;Security版本爲5。如因版本差別致使的沒法正常運行和獲取指望結果的狀況發生,能夠經過評論私信給予咱們反饋。謝謝。html

第一期 快速爲Spring Boot應用添加Spring Security

本期的任務清單

  1. 快速初始化一個Spring Boot項目
  2. 如何添加基於內存的用戶鑑權功能
  3. 如何添加基於角色的訪問控制邏輯

一、快速初始化一個Spring Boot項目

咱們推薦初學者若是對Maven的pom.xml編寫和spring各類starter依賴不熟悉的狀況下,使用Spring官方提供的項目初始化工具進行生成。start.spring.io/ 咱們的目的是初始化一個基於SpringBoot的Web應用,而且包含了Security的相關組件。因此在工具的Web界面上依賴組件部分,須要依次鍵入並選擇Web、Security和Thymeleaf三個組件依賴。 點擊生成後,瀏覽器便會自動下載一個項目工程壓縮包。 前端

https://start.spring.io/

解壓縮代碼以後,經過IDEA導入後便會獲得一個已經包含了Security的基礎SpringBoot應用。 運行DemoApplication.java,在控制檯看到應用啓動完畢後,經過瀏覽器訪問本機的127.0.0.1:8080端口,如看到提示輸入用戶名和密碼的登陸對話框,那麼就表明了Security已經成功的加載到了應用中。 java

默認登陸對話框

二、添加基於內存的用戶鑑權功能

鑑權和訪問控制的區分

鑑權和受權
首先,咱們要對Authentication和Authorization兩個詞作一個區別。在中文語義上咱們會把Authentication稱爲鑑權,用戶認證,一般是一個對根據某些信息、好比用戶名、密碼、令牌等來辨別用戶的過程。而Authorization在中文語義上能夠理解爲受權、訪問控制。爲了未來好區別咱們未來會統一把Authorization稱爲訪問控制(Access Control),由於Authorization是根據用戶的角色、權限等信息來判斷目標行爲、資源是否是能夠被對應的用戶使用、消費。 簡單的說,鑑權就是用戶登陸、受權就是權限控制。

而咱們的第二個任務就是配置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;
    }
}
複製代碼

咱們在代碼中主要完成的工做就注入了一個\color{red}{UserDetailsService}的組件。那麼什麼是\color{red}{UserDetailsService}呢?數據庫

UserDetailsService 和 UserDetails

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

InMemoryUserDetailsManager是Security提供的UserDetailsService的實現類
咱們經過新建InMemoryUserDetailsManager,而後經過createUser方法向其添加了一條用戶記錄。最終將其注入Spring框架,使其爲咱們的應用在登陸時候能夠正確的查找到咱們指望的用戶記錄。

@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頁面。

index.html的訪問頁面

三、 如何添加基於角色的訪問控制邏輯

剛剛一個任務咱們完成了Spring Security兩個主要關注點之一的鑑權功能,如今咱們就開始實現一個最簡單的訪問控制邏輯。 首先,咱們先對當前任務的目標作一個簡單的設計: 咱們將在MainController編寫兩個路徑頁面,分別是不須要訪問控制的 ""路徑和須要登陸控制的"\user"路徑。

基於url的訪問控制設計圖

編寫控制器

首先咱們根據設計將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是繼承框架提供的\color{red}{WebSecurityConfigurerAdapter},其中有過一個重要的配置方案咱們關注下框架提供的默認值實現:

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的配置主要有三點:

  1. authorizeRequests()下管理路徑訪問控制;
  2. formLogin()管理登陸表單配置;
  3. httpBasic()是否基於Http的驗證配置。 後兩點不是咱們的重點,咱們的目標是配置路徑的訪問控制,因此咱們須要在咱們本身的配置類覆寫這個方法:
@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頁面。

\user頁面
這樣咱們也完成最簡單的訪問控制配置的任務。

結尾

咱們在本期中對SpringSecurity最重要的兩個功能:用戶鑑權和訪問控制作了最簡單的實現和配置。 在下一期,咱們將對用戶鑑權部分的具體流程展開講解。讓咱們下期再見。

相關文章
相關標籤/搜索