【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,它提供了通用的內置表達式。

image-20211018143647642

  • 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() 匿名可以訪問

image-20211018153827241

  • 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注解

  • 結構

image-20211018161810965

  • 開啟支持

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

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的話,則會提示沒有權限。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM