1. 程式人生 > 其它 >【SpringBoot】---整合安全框架,實現安全認證和授權

【SpringBoot】---整合安全框架,實現安全認證和授權

【SpringBoot】---整合安全框架,實現安全認證和授權

一、SpringSecurity——Spring的安全框架

1、認識Spring Security

  • Spring Security提供了宣告式的安全訪問控制解決方案(僅支援基於Spring的應用程式),對訪問許可權進行認證和授權,基於Spring AOP特性和Servlet過濾器。

①核心概念

  • Principle:代表使用者的物件Principle(User),不僅指人類,還包括一切可以用於驗證的裝置。
  • Authority:代表使用者的角色Authority(Role),每個使用者都應該有一種角色,如管理員或者是會員。
  • Permission:代表授權,複雜的應用環境需要對角色的許可權進行描述。

Authority和Permission是兩個完全獨立的概念。沒有必然聯絡,可以自己定義各種關係。

②認證和授權

  • 驗證(authentication):
    • 建立系統使用者資訊(Principal)的過程。
    • 使用者認證一般要求使用者提供使用者名稱和密碼,系統通過使用者名稱密碼來完成認證通過或者拒絕。
    • Spring Security支援主流認證方式,包括HTTP基本認證、表單驗證、摘要認證、OpenID和LDAP等。
    • 驗證步驟:
      • 使用者使用使用者名稱和密碼登入
      • 過濾器獲取到使用者名稱、密碼,然後封裝成Authentication
      • AuthenticationManager認證token(Authentication的實現類傳遞)
      • AuthenticationManager認證成功,返回一個封裝了使用者許可權資訊的Authentication物件,使用者的上下文資訊(角色列表等等)。
      • Authentication物件賦值給當前的SecurityContext,建立這個使用者的安全上下文(通過呼叫SecurityContextHolder.getContext().setAuthentication())。
      • 使用者進行 一些受到訪問控制機制保護的操作,訪問控制機制會依據當前安全上下文資訊檢查這個操作所需要的許可權。
    • 除了利用提供的認證之外,還可以編寫自己的FIlter(過濾器),提供與那些不是基於Spring Security的驗證系統的操作。
  • 授權(authorization):
    • 在一個系統中,不同使用者具有許可權是不同的。
    • 系統會為不同的使用者分配不同的角色,而每個角色則對應一系列的許可權。
    • 對Web資源的保護,最好的辦法是使用過濾器。對方法呼叫的保護,最好的方法是使用AOP。

③模組

  • 模組核心——Spring-security-core.jar:包含核心驗證和訪問控制類和介面,以及支援遠端配置的基本API。

  • 遠端呼叫——spring-security-remoting.jar

  • 網頁——spring-security-web.jar

  • 配置——spring-security-config.jar

  • LDAP(Lightweight Directory Access Protocol):spring-security-ldap

  • ACL(訪問控制列表)

  • CAS(中央認證服務)

  • OpenID(身份認證系統)

2、核心類

①SecurityContext

SecurityContext中包含當前正在訪問系統的使用者的詳細資訊,他只有以下兩種方法。

  • getAuthentication():獲取當前經過身份驗證的主題或者身份驗證的請求令牌。
  • setAuthentication():更改或者刪除當前已經驗證的主體身份驗證資訊。

SecurityContext的資訊是由SecurityContextHolder來處理的。

②SecurityContextHolder

用來儲存SecurityContext。最常用的是通過getContext()方法,用來獲得當前的SecurityContext。

  • 獲取當前使用者的SecurityContext
    • 使用Authentication物件來描述當前使用者的相關資訊。Security-ContextHolder中持有的時當前使用者的SecurityContext,而SecurityCOntext持有的時代表當前使用者相關資訊的Authentication的引用。
    • 這個Authentication物件不需要自己建立,Spring Security會自動建立,然後賦值給當前的SecurityContext。
    • 在程式的任何地方,可以通過如下方式獲取到當前使用者的使用者名稱
public String getCurrentUsername(){
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    if(principal instanceof UserDetails){
        return ((UserDetatils) princeipal).getUsername();
    }
    if(principal instanceof Principal){
        return ((Principal) principal).getName();
    }
    return String.valueOf(principal);
}
  • getAuthentication()方法會返回認證資訊。
  • getPrincipal()方法返回身份資訊,它是UserDetails對身份資訊的封裝。

獲取當前使用者的使用者名稱,最簡單的方式如下:

public String getCurrentUsername(){
	return SecurityContextHolder.getContext().getAuthentication().getName();
}

③PrividerManager

會維護一個認證列表,來處理不同認證方式的認證,因為系統可能會存在多種認證方式。比如手機號、使用者名稱密碼、郵箱。

如果認證結果不是null,說明成功,存在SecurityContext中。如果不成功,丟擲ProviderNotFoundException異常。

④DaoAuthenticationProvider

它是AuthenticationProvider最常用的實現,用來獲取使用者提交的使用者名稱和密碼,並進行正確性比對。如果正確,則返回資料庫中的使用者資訊。

⑤UserDetails

​ 它是Spring Security的使用者實體類,包含使用者名稱、密碼、許可權等資訊。SPring Security預設實現了內建的User類,供Spring Security安全認證使用。也可以自己實現。

UserDetails介面和Authentication介面很類似,都擁有username和authorites。一定要區分清楚Authentication的getCredentials()與UserDetatils中的getPassword()。前者是使用者提交的密碼憑證,不一定正確,或者資料庫不一定存在。後者是使用者正確地密碼,認證器要進行比對的就是兩者是否相同。

⑥UserDetailsService

使用者資訊通過UserDetailsService介面來載入。該介面的唯一方法是loadUserByUsername(String username),用來根據使用者名稱載入相關資訊 。這個方法返回值是UserDetatils介面,其中包含了使用者的資訊,包括使用者名稱、密碼、許可權、是否啟用等

⑦GrantedAuthority

定義了getAuthority()方法,返回一個字串,表示對應許可權的字串。

⑧Filter

  • SecurityContextPersistenceFilter

    • 從SecurityContextRepository中取出使用者認證資訊,為了避免每次請求都要查詢認證資訊,它會從Session中取出已經認證的使用者資訊,然後把它放入SecurityContextHolder中,便於其他Filter使用。
  • WebAsyncManagerIntegrationFilter

    • 繼承了SecurityContext和WebAsyncManager,把SecurityContext設定到非同步執行緒,使其也能獲取到使用者上下文認證資訊。
  • HeaderWriterFilter

    • 對請求的Header新增相應的資訊
  • CsrfFilter

    • 跨域請求偽造過濾器,通過客戶端傳來的token與伺服器端儲存的token進行對比,來判斷請求的合法性。
  • LogoutFilter

    • 匹配登出URL。匹配成功之後,退出使用者,並且清楚認證資訊。
  • UsernamePasswordAuthenticationFilter

    • 登入認證過濾器,預設是對"/login"的POST請求進行認證。該方法會呼叫attemptAuthentication,嘗試獲取一個Authentication認證物件,以儲存認證資訊,然後轉向下一個Filter,最後呼叫successfulAuthentication執行認證後的事件。
  • AnonymousAuthenticationFilter

    • 如果SecurityContextHolder中的認證資訊為空,則會建立一個匿名使用者到SecurityCOntextHolder中
  • SessionManagementFilter

    • 持久化登入的使用者資訊。使用者資訊會被儲存到Session、Cokkie,或者Redis中。

二、配置Spring Security

1、繼承WebSecurityConfigurerAdapter

​ 通過重寫抽象介面WebSecurityConfigurerAdapter,再加上註解@EnableWebSecurity可以實現Web的安全配置。

WebSecurityConfigurerAdapter Config模組一共有3個builder(構造程式)

  • AuthenticationManagerBuilder:認證相關builder,用來配置全域性的認證相關的資訊。它包含AuthenticationProvider和UserDetatilsService,前者是認證服務提供者,後者是使用者詳情查詢服務。
  • HttpSecurity:進行許可權控制規則相關配置
  • WebSecurity:進行全域性請求忽略規則配置、HttpFirewall配置、debug配置、全域性SecurityFilterChain配置。

配置安全、通常要重寫以下方法:

//通過auth物件的方法新增身份驗證
protected void configure(AuthenticationManagerBuilder auth) throws Exception{}
//通常用於設定忽略許可權的靜態資源
public void configure(WebSecurity web) throws Excepption{}
//通過HTTP物件的authorizeRequests()方法定義URL訪問許可權。預設為formLogin()提供一個簡單的登入驗證頁面
protected void configure(HttpSecurity httpSecurity) throws Exception{}

2、配置自定義策略

需要繼承WebSecurityConfigurerAdapter,然後重寫其方法。

package com.example.demo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
//指定為SpringSecurity配置類,如果是WebFlux,則需要啟動@EnableWebFluxSecurity
@EnableWebSecurity
//如果要啟用方法安全設定,則開啟此項
@EnableGlobalMethodSecurity(proPostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
    public void configure(WebSecurity web) throws Exception{
        //不攔截靜態資源
        web.ignoring().antMatchers("/static/**");
    }
    @Bean
    public PasswordEncoder passwordEncoder(){
        //使用BCrypt加密
        return new BCryptPasswordEncoder();
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception{
        http.formLogin().usernameParameter("uname").passwordParameter("pwd").loginPage("/admin/login").permitAll()
            .add()
            .authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            //除上面外的所有請求全部需要鑑權認證
            .anyRequest().authenticated();
        http.logout().permitAll();
        http.rememberMe().rememberMeParameter("rememberme");
        //處理異常,拒接訪問就重定向到403頁面
        http.exceptionHandling().accessDeniedPage("/403");
        http.logout().logoutSuccessUrl("/");
        http.csrf().ignoringAntMatchers("/admin/upload");
    }
}
  • authorizeRequests():定義哪些URL需要被保護,哪些不需要被保護。

  • .antMatchers("/admin/**").hasRole("ADMIN"):定義/admin/下的所有URL。只有擁有admin角色的使用者才有訪問許可權。

  • formLogin():自定義使用者登入驗證的頁面

  • http.csrf():配置是否開啟CSRF保護,還可以在開啟之後指定忽略的介面

    • 如果開啟了CSRF,則一定要在驗證頁面加入一下程式碼來傳遞token的值:

    • <head>
          <meta name="_csrf" th:content = "${_csrf.token}"/>
          <meta name = "_csrf_header" th:content = "${_csrf.headerName}"/>
      </head>
      
    • 如果要提交表單,則需要在表單中新增一下程式碼來提交token的值

    • <input type = "hidden" th:name = "${_csrf.prameterName}" th:value = "${_csrf.token}">
      
    • http.rememberMe():記住我功能,可以指定引數

    • 使用的時候,新增如下程式碼

    • <input class = "i-checks" type = "checkbox" name = "rememberme"/>&nbsp;&nbsp;記住我
      

3、配置加密方式

​ 預設的加密方式是BCrypt,只要在安全配置類配置即可使用:

@Bean
public PaswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();//使用BCrypt加密
}

在業務程式碼中,可以使用以下方式,對密碼進行加密:

BCryptPasswordEncoder encoder = new BcryPasswordEncoder();
String encodePassword = encoder.encode(password);

4、自定義加密規則

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
    auth.userDetatilsService(UserService()).passwordEncoder(new PasswordEncoder(){
        @Override
        public String encode(CharSequence charSequence){
            return MD5Util.encode((String) charSequence);
        }
        @Override
        public boolean matches(CharSequence charSequence,String s){
            return s.equals(MD5Util.encode((String) charSequence));
        }
    })
}

5、配置多使用者系統

​ 一個完整的系統一般包括多種使用者系統,比如“後臺管理系統+前端使用者系統”。Spring Security預設只提供一個使用者系統,所以,需要通過配置來實現多使用者系統。

​ 比如,如果要構建一個前臺會員系統,則可以通過下面的步驟來實現。

①構建UserDetailsService使用者資訊服務介面

構建前端使用者UserSecurityService類,並且繼承UserDetailsService。

public class UserSecurityService implements UserDetailService{
    @Autowired
    private UserRepository userRepository;
    @Override
    public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException{
        User user = userRepository.findByName(name);
        if(user == null){
            User mobileUser = userRepository.findByMobile(name);
            if(mobileUser==null){
                User emailUser = userRepository.findByEmail(name);
                if(emailUser == null){
                    throw new UsernameNotFoundException("使用者名稱,郵箱或手機號不存在!");
                }else{
                    user = userRepository.findByEmail(name);
                }
            }else{
                user = userRepository.findByMobile(name);
            }
        }else if("locked".equals(user.getStatus())){
            //被鎖定,無法登陸
            throw new LockedException("使用者被鎖定");
        }
        return user;
    }
}

②進行安全配置

​ 在繼承WebSecurityConfigurerAdapter的Spring Security配置類中,配置UserSecurity-Service類。

@Bean
UserDetailsService UserService(){
    return new UserSecurityService();
}

如果要加入後臺管理系統,只需要重複上面的步驟就可以了。

6、獲取當前登入使用者資訊的幾種方式

①在Controller中獲取---3種方式

@GetMapping("userinfo")
public String getProduct(Principal principal,Authentication authentication,HttpServletRequest httpServletRequest){
    //通過Principal引數獲取
    String username = pringcipal.getName();
    //通過Authentication引數獲取
    String userName2 = authentication.getName();
    //通過HttpServletRequest獲取
    Principal httpServletRequestUserPrincipal = httpServletrequest.getUserPrincipal();
    String userName3 = httpServletRequestUserPrincipal.getName();
    return username;
}

②在Bean中獲取

Authentication authentication = SecurityContextHoler.getContext().getAuthentication();
if(!(authentication instanceof AnonymousAuthenticationToken)){
    String username = authentication.getName();
    return username;
}

如果上面的程式碼獲取不到,可能是下面原因:

  • 必須在繼承WebSecurityConfigurerAdapter的類中http.antMatcher("/*")的鑑權URI範圍之內。
  • 添加了SpringSecurity的依賴,但是版本不對。

7、例項:用Spring Security實現後臺登入及許可權認證功能

①引入依賴

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-springsecurity5</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

②建立許可權開放的頁面

<!DOCTYPE html><html lang="en" xmlns:th="http://www.thymeleaf.org"
                     xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head><title>Spring Security 案例</title></head>
<body>
<h1>Welcome!</h1>
<p><a th:href="@{/home}">會員中心</a></p>
</body></html>

③建立需要許可權驗證的頁面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head><title>home</title></head>
<body>
<p>會員中心</p>
<p th:inline="text">Hello <span sec:authentication="name"></span></p>
<form th:action="@{/logout}" method="post">
    <input type="submit" value="登出"/>
</form>
</body></html>

④配置Spring Security

  • 配置Spring MVC

    • package com.example.demo.config;
      
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
      import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
      
      @Configuration
      public class WebMvcConfig implements WebMvcConfigurer {
          @Override
          public void addViewControllers(ViewControllerRegistry registry) {
              //設定登入處理操作
              registry.addViewController("/home").setViewName("springsecurity/home");
              registry.addViewController("/").setViewName("springsecurity/welcome");
      
              registry.addViewController("/login").setViewName("springsecurity/login");
      
          }
      }
      
      
  • 配置Spring Security

    • package com.example.demo.config;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
      import org.springframework.security.config.annotation.web.builders.HttpSecurity;
      import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
      import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
      import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
      
      @Configuration
      @EnableWebSecurity//指定為Spring Security配置類
      public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
      
          //授權
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http.authorizeRequests()
                  	//這些url所有人都可以訪問
                      .antMatchers("/", "/welcome", "/login").permitAll()
                      .anyRequest().authenticated()
                      .and()
                      .formLogin().loginPage("/login").defaultSuccessUrl("/home")
                      .and()
                      .logout().permitAll();
          }
      
      
          //認證
          @Autowired
          public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
              //記憶體認證
              auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                      //在SpringSecurity5之後,必須要使用一種加密的方式。
                  .withUser("admin").password("$2a$10$Q21imUyxDeshQ2tQBUfJKuBHbmuyTsZYoCMRmGi5UcOIavevauZwS").roles("USER");//密碼是lzhonghua
          }
      }
      
      • configure(HttpSecurity):定義了哪些URL路徑應該被攔截。
      • configureGlobal(AuthenticationManagerBuilder):在記憶體中配置一個使用者,admin/lzhonghua,這個使用者擁有User角色

⑤建立登入頁面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head><title>Spring Security Example </title></head>
<body>
<div th:if="${param.error}">
    無效的使用者名稱或者密碼
</div>
<div th:if="${param.logout}">
    你已經登出
</div>
<form th:action="@{/login}" method="post">
    <div><label> 使用者名稱 : <input type="text" name="username"/> </label></div>
    <div><label> 密碼: <input type="password" name="password"/> </label></div>
    <div><input type="submit" value="登入"/></div>
</form>
</body>
</html>

⑥測試許可權

啟動專案

訪問http://localhost:8080

三、許可權控制方式

1、Spring EL許可權表示式

​ Spring Security支援在定義URL訪問或方法訪問許可權的時候,使用Spring EL表示式。根據表示式返回的值(true或false)來授權或者拒絕對應的許可權。Spring Security可用表示式物件的基類是SecurityExpressionRooot,它提供了通用的內建表示式。

  • isAuthenticated():表示當前使用者是否已經登入認證成功了。
  • isFullyAuthenticated():如果當前使用者既不是匿名使用者,又不是通過Remember-Me自動登入的,則返回true。

在檢視模板檔案中,可以通過表示式控制顯示許可權:

<p sec:authorize="hasRole('ROLE_ADMIN')">管理員</p>
<p sec:authorize="hasRole('ROLE_USER')">普通使用者</p>

在WebSecurityConfig中新增兩個記憶體使用者用於測試,角色分別是ADMIN,USER:

.withUser("admin").password("123456").roles("ADMIN")
.and().withUser("user").password("123456").roles("USER");

使用者admin登入,顯示管理員,user登入則顯示普通使用者。

然後再WebSecurityConfig中加入下面的URL許可權配置

.antMatchers("/home").hasROle("ADMIN")

當用admin使用者訪問"home"頁面時能正常訪問,而用user使用者訪問時候則會提示“403禁止訪問”。這就是通過URL控制權限的辦法。

2、通過表示式控制URL許可權

​ 如果要限定某類使用者訪問某個URL,則可以通過SpringSecurity提供的基於URL的許可權控制來實現。

​ SpringSecurity提供的保護URL的方法是重寫configure(HttpSecurity http)方法

方法名 用途
access(String) SpringEl表示式結果為true時候可以訪問
anonymous() 匿名可以訪問
  • hasRole(String role):限制單個角色訪問。在Spring Security中,角色是被預設增加“ROLE_"字首的,所以角色"ADMIN"代表"ROLE_ADMIN"
  • hasIpAddress(String ipaddressExpression):用於限制IP地址或子網

3、通過表示式控制方法許可權

​ 要想在方法上使用許可權控制,則需要使用啟用方法安全設定的註解@EnableGlobalMethodSecurity()。它預設是禁用的,需要在繼承WebSecurityConfigurerAdapter的類上加註解來啟用,還需要配置啟用的型別,它支援開啟如下三種類型。

  • @EnableGlobalMethodSecurity(jsr250Enabled=true):開啟JSR-250
  • @EnableGlobalMethodSecurity(prePostEnabled=true):開啟prePostEnabled
  • @EnableGlobalMethodSecurity(securedEnabled=true):開啟secured

JSR-250

  • JSR是Java Specification Requests的縮寫,是Java規範提案。不同的JSR其功能是不一樣的,比如JSR-303是為了資料的驗證提供一些API。

  • JSR-250是用於提供方法安全設定的,它主要提供了註解@RolesAllowed。

  • 主要方法:

    • @DenyAll:拒絕所有訪問
    • RolesAllowes({"USER","ADMIN"}):該方法只要具有"USER"/“ADMIN"任意一種許可權就可以訪問。
    • @PermitAll:允許所有訪問

prePOSTEnabled

​ prePostEnabled註解,也是基於表示式的註解,並且可以通過繼承GlobalMethodSecurityConfiguration類來實現自定義功能。如果訪問沒有訪問方法的許可權,則會丟擲AccessDeniedException。

  • @PreAuthorize

    • 它在方法執行之前執行:

      • 限制userId的值是否等於principal中儲存的當前使用者的userId,或者當前使用者是否具有ROLE_ADMIN許可權。

        @PreAuthorize("#userId==authentication.principal.userId or hasAuthority('ADMIN')")
        
      • 限制擁有ADMIN角色才能執行

        @PreAuthorize("hasRole('ROlE_ADMIN')")
        
      • 限制擁有ADMIN角色或者USER角色才能執行

        @PreAuthorize("hasRole('ROlE_ADMIN') or hasRole('ROLE_ADMIN')")
        
      • 限制只能查詢id小於3的使用者才能執行

        @PreAuthorize("#id<3")
        
      • 限制只能查詢自己的資訊,這裡一定要在當前頁面經過許可權驗證,否則會報錯

        @PreAuthorize("principal.username.equals(#username)")
        
      • 限制使用者名稱只能為long的使用者

        @PreAuthorize("#user.name.equals('long')")
        
    • 對於低版本的Spring Security,添加註解之後還需要將AuthenticationManager定義為Bean

      • @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception{
            return super.authenticationManagerBean();
        }
        @Autowired
        AuthenticationManager authenticationManager;
        
  • @PostAuthorize

    • 表示在方法執行之後執行,有時候需要在方法呼叫完成之後才進行許可權檢查。可以通過這個註解實現

    • 不能控制方法是否能被呼叫

    • 可以呼叫方法的返回值,如果EL為false,那麼該方法已經執行完畢,可能會回滾。EL變數returnObject表示返回的物件

      @PostAuthorize("returnObject.userId==authentication.principal.userId or hasPermission(returnObject,'ADMIN')");
      
  • @PreFilter

    • 表示在方法執行之前執行,它可以呼叫方法的引數,然後對引數值進行過濾、處理和修改。EL變數filterObject表示引數,如果有多個引數,則使用filterTarget註解引數。方法引數必須是集合或者陣列
  • @postFilter

    • 表示在方法執行之後執行,而且可以呼叫方法的返回值,然後對返回值進行過濾、處理或者修改,並且返回。EL變數returnObject表示返回的物件。方法需要返回集合或者陣列
    • 如使用@PreFilter和PostFilter時候,Spring Security將會移除使對應使得表示式結果為false的元素。
    • 當Filter標註的方法擁有多個集合型別的引數時候,需要通過filterTarget屬性指定當前是針對哪個引數進行過濾的。

secureEnabled

​ 開啟securedEnabled支援後,可以使用註解@Secured來認證使用者是否有許可權訪問

@Secured("IS_AUTHENTICATED_ANONYMOUSLY") public Accounet readAccount(Long id);
@Secured("ROLE_TELLER")

4、例項:使用JSR-250註解

  • 結構
  • 開啟支援

    在安全配置類中 ,啟用註解。

package com.example.demo.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;

import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/*
 * @EnableGlobalMethodSecurity(securedEnabled=true)
 * 開啟@Secured 註解過濾許可權
 *
 * @EnableGlobalMethodSecurity(jsr250Enabled=true)
 * 開啟@RolesAllowed 註解過濾許可權
 *
 * @EnableGlobalMethodSecurity(prePostEnabled=true)
 * 使用表示式時間方法級別的安全性 4個註解可用
 * -@PreAuthorize 在方法呼叫之前,基於表示式的計算結果來限制對方法的訪問
 * -@PostAuthorize 允許方法呼叫,但是如果表示式計算結果為false,將丟擲一個安全性異常
 * -@PostFilter 允許方法呼叫,但必須按照表達式來過濾方法的結果
 * -@PreFilter 允許方法呼叫,但必須在進入方法之前過濾輸入值
 **/
@Configuration
@EnableWebSecurity//指定為Spring Security配置類
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {



    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/*", "/user/*").permitAll()

                .anyRequest().authenticated()
                .and()
                .logout().permitAll();
    }

}
  • UserService介面
package com.example.demo.service;

public interface UserService {
    public String addUser();
    public String updateUser() ;
    public String deleteUser() ;
}
  • UserServiceImpl
package com.example.demo.service;

import com.example.demo.service.UserService;
import org.springframework.stereotype.Service;

import javax.annotation.security.RolesAllowed;

@Service
public class UserServiceImpl implements UserService {
    @Override
    public String addUser() {
        System.out.println("addUser");
        return null;
    }

    @Override
    @RolesAllowed({"ROLE_USER","ROLE_ADMIN"})
    public String updateUser() {
        System.out.println("updateUser");
        return null;
    }

    @Override
    @RolesAllowed("ROLE_ADMIN")
    public String deleteUser() {
        System.out.println("delete");
        return null;
    }
}
  • 編寫控制器
package com.example.demo.controller;

import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("user")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/addUser")
    public void addUser() {
        userService.addUser();
    }

    @GetMapping("/updateUser")
    public void updateUser() {
        userService.updateUser();
    }

    @GetMapping("/delete")
    public void delete() {
        userService.deleteUser();

    }
}

訪問adduser可以,訪問delete和update的話,則會提示沒有許可權。