Spring Security權限框架理論與簡單Case

Spring Security權限管理框架介紹

Spring Security 提供了基於javaEE的企業應用軟件全面的安全服務。這裏特別強調支持使用Spring框架構件的項目,Spring框架是企業軟件開發javaEE方案的領導者。若是你尚未使用Spring來開發企業應用程序,咱們熱忱的鼓勵你仔細的看一看。熟悉Spring特別是一來注入原理兩幫助你更快更方便的使用Spring Security。css

人們使用Spring Secruity的緣由有不少,單大部分都發現了javaEE的Servlet規範或EJB規範中的安全功能缺少典型企業應用場景所需的深度。提到這些規範,重要的是要認識到他們在WAR或EAR級別沒法移植。所以若是你更換服務器環境,這裏有典型的大量工做去從新配置你的應用程序員安全到新的目標環境。使用Spring Security 解決了這些問題,也爲你提供許多其餘有用的,可定製的安全功能。html

正如你可能知道的兩個應用程序的兩個主要區域是「認證」和「受權」(或者訪問控制)。這兩個主要區域是Spring Security 的兩個目標。「認證」,是創建一個他聲明的主題的過程(一個「主體」通常是指用戶,設備或一些能夠在你的應用程序中執行動做的其餘系統)。「受權」指肯定一個主體是否容許在你的應用程序執行一個動做的過程。爲了抵達須要受權的目的,主體的身份已經有認證過程創建。這個概念是通用的而不僅在Spring Security中。java

Spring Security主要的組件圖:
Spring Security權限框架理論與簡單Case程序員

在身份驗證層,Spring Security 的支持多種認證模式。這些驗證絕大多數都是要麼由第三方提供,或由相關的標準組織,如互聯網工程任務組開發。另外Spring Security 提供本身的一組認證功能。具體而言,Spring Security 目前支持全部這些技術集成的身份驗證:web

  • HTTP BASIC 認證頭 (基於 IETF RFC-based 標準)
  • HTTP Digest 認證頭 ( IETF RFC-based 標準)
  • HTTP X.509 客戶端證書交換 ( IETF RFC-based 標準)
  • LDAP (一個很是常見的方法來跨平臺認證須要, 尤爲是在大型環境)
  • Form-based authentication (用於簡單的用戶界面)
  • OpenID 認證
  • Authentication based on pre-established request headers (such as Computer Associates Siteminder) 根據預先創建的請求有進行驗證
  • JA-SIG Central Authentication Service (CAS,一個開源的SSO系統 )
  • Transparent authentication context propagation for Remote Method Invocation (RMI) and HttpInvoker (Spring遠程協議)
  • Automatic "remember-me" authentication (你能夠勾選一個框以免預約的時間段再認證)
  • Anonymous authentication (讓每個未經驗證的訪問自動假設爲一個特定的安全標識)
  • Run-as authentication (在一個訪問應該使用不一樣的安全標識時很是有用)
  • Java Authentication and Authorization Service (JAAS)
  • JEE container autentication (因此若是願你以能夠任然使用容器管理的認證)
  • Kerberos
  • Java Open Source Single Sign On (JOSSO) *
  • OpenNMS Network Management Platform *
  • AppFuse *
  • AndroMDA *
  • Mule ESB *
  • Direct Web Request (DWR) *
  • Grails *
  • Tapestry *
  • JTrac *
  • Jasypt *
  • Roller *
  • Elastic Path *
  • Atlassian Crowd *
  • Your own authentication systems (see below)

Spring Security5.x官方文檔地址以下:spring

https://docs.spring.io/spring-security/site/docs/5.0.7.RELEASE/reference/htmlsingle/
https://docs.spring.io/spring-security/site/docs/5.0.7.RELEASE/api/數據庫


Spring Security經常使用的11個權限攔截器

Spring Security權限框架理論與簡單Case

SecurityContextPersistenceFilter:api

  • 這個過濾器位於頂端,是第一個起做用的過濾器
  • 驗證用戶session是否存在,存在則放到SecurityContextHolder中,不存在則建立後到SecurityContextHolder中
  • 另外一個做用是在過濾器執行完畢後清空SecurityContextHolder中的內容

LogoutFilter:安全

  • 在用戶發出註銷請求時,清除用戶的session以及SecurityContextHolder中的內容

AbstractAuthenticationProcessingFilter:bash

  • 處理from表單登陸的過濾器

DefaultLoginPageGeneratingFilter:

  • 用來生成一個默認的登陸頁面

BasicAuthenticationFilter:

  • 用於Basic驗證

SecurityContextHolderAwareRequestFilter:

  • 用於包裝用戶的請求
  • 目的是爲後續的程序提供一些額外的數據

RememberMeAuthenticationFilter:

  • 當用戶cookie中存在RememberMe標記時,會根據標記自動實現用戶登陸,並建立SecurityContext,授予相應的權限

AnonymousAuthenticationFilter:

  • 保證操做統一性,當用戶沒有登陸時,默認爲用戶分配匿名用戶的權限,能夠選擇關閉匿名用戶

ExceptionTranslationFilter:

  • 處理FilterSecurityInterceptor中所拋出的異常,而後將請求重定向到相應的頁面,或響應錯誤信息。也就是做爲一個處理全局異常的Filter

SessionManagementFilter:

  • 用於防護會話僞造×××,會銷燬用戶的session,並從新生成一個session

FilterSecurityInterceptor:

  • 用戶的權限控制都包含在這裏
  • 若是用戶未登錄就會拋出用戶未登錄的異常
  • 若是用戶已登陸可是沒有訪問當前資源的權限,就會拋出拒絕訪問異常
  • 若是用戶已登陸並具備訪問當前資源的權限,則放行

以上就是Spring Security經常使用的11個權限攔截器,那麼這些攔截器是按什麼樣的順序執行的呢?這就須要先了解一下FilterChainProxy這個過濾器鏈代理類了:

  • FilterChainProxy能夠按照指定的順序調用一組Filter,使這組Filter既能完成驗證受權的本職工做,又能享用Spring IOC的功能,來方便的獲得其餘依賴的資源

基於SpringBoot的SpringSecurity環境快速搭建

打開IDEA,建立一個SpringBoot項目:
Spring Security權限框架理論與簡單Case
Spring Security權限框架理論與簡單Case

勾選相應的模塊:
Spring Security權限框架理論與簡單Case

在項目中新建一個config包,在該包下建立 SpringSecurityConfig 配置類,用於配置Spring Security的攔截規則。代碼以下:

package org.zero.security.securitydemo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @program: security-demo
 * @description: Spring Security 配置類
 * @author: 01
 * @create: 2018-08-29 23:20
 **/
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 定義一個簡單的訪問規則
        http.authorizeRequests()
                .antMatchers("/").permitAll()  // 容許任意訪問根路徑
                .anyRequest().authenticated()  // 除根路徑之外的請求都須要驗證
                .and().logout().permitAll()  // 容許任意訪問註銷路徑
                .and().formLogin();  // 容許表單登陸

        // 禁用默認的csrf驗證
        http.csrf().disable();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // 不攔截靜態資源的訪問
        web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
    }
}

而後新建一個controller包,在該包中建立 DemoController 控制器類,用於開啓一些接口進行測試。代碼以下:

package org.zero.security.securitydemo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @program: security-demo
 * @description: spring security demo
 * @author: 01
 * @create: 2018-08-29 23:14
 **/
@RestController
public class DemoController {

    @GetMapping("/")
    public String demo(){
        return "Hello Spring Security";
    }

    @GetMapping("/hello")
    public String hello(){
        return "Hello World";
    }
}

啓動項目,訪問根目錄,正常輸出了相應的字符串:
Spring Security權限框架理論與簡單Case

而訪問/hello,就會跳轉到登陸頁面,須要進行驗證,這就表明SpringSecurity的配置:
Spring Security權限框架理論與簡單Case


基於SpringSecurity權限管理Case實操

Case一、簡單的登陸:

SpringSecurity自帶有一套基於內存的驗證,這樣咱們僅須要實現簡單的登陸功能的時候,就不須要額外去建立數據庫了。在 SpringSecurityConfig 類中,加入以下方法:

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().  // 使用基於內存的認證方式
                passwordEncoder(new BCryptPasswordEncoder()).  // 設置密碼的加密方式
                withUser("admin").  // 設置用戶名稱
                password(new BCryptPasswordEncoder().encode("123456")).  // 設置密碼
                roles("ADMIN");  // 自定義該用戶的角色
    }
    ...
}

重啓項目,當訪問受控制的資源時,就會跳轉到以下登陸頁面,輸入設定好的用戶名和密碼:
Spring Security權限框架理論與簡單Case

訪問成功:
Spring Security權限框架理論與簡單Case

訪問logout接口能夠退出登陸:
Spring Security權限框架理論與簡單Case


Case二、有指定的角色,每一個角色有指定的權限:

即使是簡單的登陸,也可能會遇到有一些資源須要管理員角色才能訪問。因此咱們來看看如何限定一個資源只能被管理員用戶訪問。在configure方法中,增長一個普通用戶,代碼以下:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().
            passwordEncoder(new BCryptPasswordEncoder()).
            withUser("admin").
            password(new BCryptPasswordEncoder().encode("123456")).
            roles("ADMIN");

    auth.inMemoryAuthentication().
            passwordEncoder(new BCryptPasswordEncoder()).
            withUser("user").
            password(new BCryptPasswordEncoder().encode("user")).
            roles("USER");
}

在DemoController類中增長一個接口,並指定這個接口只能被admin角色的用戶訪問。代碼以下:

@RestController
// 開啓驗證
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class DemoController {

    // 指定該接口只能被ADMIN角色的用戶訪問,ROLE_這個前綴是固定要寫的
    @PreAuthorize("hasRole('ROLE_ADMIN')")  
    @GetMapping("/roleAuth")
    public String role(){
        return "admin auth";
    }
    ...
}

重啓項目,登陸非admin角色的用戶:
Spring Security權限框架理論與簡單Case

訪問roleAuth接口,會返回403錯誤:
Spring Security權限框架理論與簡單Case

登陸admin用戶,訪問roleAuth接口成功:
Spring Security權限框架理論與簡單Case

@PreAuthorize裏的表達式可使用 and 、or這種運算符,例如:

@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_ROOT')")

除了@PreAuthorize註解外,還有:

  • @PostAuthorize:方法執行完後再進行角色驗證
  • @PreFilter:方法執行前進行驗證,用於過濾集合類型的參數或返回值
  • @PostFilter:方法執行後進行驗證,用於過濾集合類型的參數或返回值

Case三、自定義密碼加密:

咱們能夠自定義本身的加密方式去作密碼的加密及匹配,我這裏使用MD5做爲演示。首先建立一個 MyPasswordEncoder 類並實現 PasswordEncoder 接口。具體代碼以下:

package org.zero.security.securitydemo.encoder;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.DigestUtils;

/**
 * @program: security-demo
 * @description: 自定義密碼加密器
 * @author: 01
 * @create: 2018-09-07 21:43
 **/
public class MyPasswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence charSequence) {
        // 使用Spring自帶的工具進行MD5加密
        return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {        
        // 驗證密碼加密後是否一致
        return encode(rawPassword).equals(encodedPassword);
    }
}

使用咱們本身自定義的密碼加密器,修改configure方法的代碼以下:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().
            passwordEncoder(new MyPasswordEncoder()).
            withUser("admin").
            password(new MyPasswordEncoder().encode("123456")).
            roles("ADMIN");

    auth.inMemoryAuthentication().
            passwordEncoder(new MyPasswordEncoder()).
            withUser("user").
            password(new MyPasswordEncoder().encode("user")).
            roles("USER");
}

Case四、參數驗證:

經過@PreAuthorize註解,咱們能夠在方法執行前,進行權限參數的驗證。例如我要驗證id小於時,且username參數的值和當前登陸的用戶名一致。代碼以下:

@PreAuthorize("#id<10 and principal.username.equals(#username)")
@GetMapping("/check_info")
public String checkInfo(Integer id, String username) {
    return "success";
}

若是參數是一個對象也能夠進行校驗,代碼以下:

@PreAuthorize("#user.username.equals('admin')")
@GetMapping("/check_user")
public String checkUser(User user) {
    return "success";
}

總結

優勢:

  • 提供了一套安全框架,並且這個框架是能夠用的
  • 提供了不少用戶認證功能,實現相關接口便可,節約大量開發工做
  • 基於Spring,使得它易於集成到Spring項目中,且封裝了許多方法

缺點:

  • 配置文件多,角色被 「編碼」 到配置文件或源文件中,RBAC不明顯
  • 對於系統中用戶、角色、權限之間的關係,沒有可操做的界面
  • 大數據量的狀況下,幾乎不可用
相關文章
相關標籤/搜索