【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的话,则会提示没有权限。

posted @ 2021-10-19 15:41  DarkerG  阅读(788)  评论(0编辑  收藏  举报