Spring sercrity 深入浅出

Spring sercrity

spring security 的学习过程

创建人:李浩杰
创建时间:2022/12/23
更新时间;2022/12/23
版本:0.0.1

环境介绍:

jdk:1.8
idea:2022.3
maven:3.86
springBoot:2.4.5
mysql: 5.8

案例:

使用瑞吉外卖,为其搭建Spring securty 框架。源码传送门

入门

引入坐标即可

<!-- spring security 坐标-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

引入坐标后访问资源将会跳转到spring security 自带的一个登录页面,进行登录 默认用户名:username 密码:在控制台打印

认证

一、自定义登录页面

基于入门案例,我们已经简单应用spring security 框架,但是表单提交的页面是框架生成的。那么我们该如何换成自己的登录页面呢?

1、创建spring security 配置类

/**
 * spring security 配置类
 *
 * @author lihaojie
 * @date 2022/12/22 16:59
 **/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
    /**
     * 身份安全管理器
     * 
     * @param auth
     * @return void
     * @author lihaojie
     * @date 2022/12/23 20:22
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
       
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
      super.configure(web);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        /**
         * 登录认证采用basic认证:通过弹出对话框的形式提示输入密码,并将账户密码以basic64的形式加密传输
         * 因为这种加密方式可以破解不安全,所以更多使用表单认证(默认)。
         */
        /* http.httpBasic()
                 //所有请求都需要经过认证才能通过
                 .and().authorizeRequests().anyRequest().authenticated();*/
        //开启表单

        http    .formLogin()
                .loginPage("/backend/login.html")  //自定义登录页面
                .loginProcessingUrl("/login")  //自定义登录页面所提交的路径
                .defaultSuccessUrl("/backend/index.html",true)  //登录成功后跳转到的页面,true:不管之前的访问地址都跳转到这里
                .passwordParameter("password")
                .usernameParameter("username")
                .and()
                .authorizeRequests().antMatchers("/backend/login.html").permitAll() //放行登录页面
                .anyRequest().authenticated();

        http.csrf().disable();
        http.headers().frameOptions().disable();
    }
}

二、基于数据库认证

到目前为止,我们一直在使用spring security 默认的username与password。但是我们想基于数据库中的username与password做认证。

1、实现MyUserDetailsServiceService

MyUserDetailsServiceService继承了UserDetailsServiceService,并重写loadUserByUsername方法。此方法内完成从数据库获根据username获取账户信息以及权限等信息,并将其封装为UserDetails并返回。如何封装呢?首先new 一个org.springframework.security.core.userdetails.User并将其赋值给UserDetails。返回的UserDetails被框架接受并自己将数据库的用户密码与前台传入的做对比。

/**
 * 使用数据库账号登录
 *
 * @author lihaojie
 * @date 2022/12/23 14:42
 **/
@Service
public class MyUserDetailsServiceService implements UserDetailsService {
    @Autowired
    private EmployeeService employeeService;
    /**
     * 设置为从数据库获取账户与密码
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		
        //1.通过employeeService查看数据库是否有吃用户名的员工
      
        Employee employeeDb=employeeService.findByUsername(username);
        //如果没有则抛出异常
        if(employeeDb==null){
            throw new UsernameNotFoundException("用户没找到:"+username);
        }
       
        //2、创建UserDetails并返回
        
       		 //参数3: 权限集合
        Collection<? extends GrantedAuthority> authorities =new ArrayList<>();
        UserDetails userDetails = new org.springframework.security.core.userdetails.User
                (username,
                "{noop}"+ employeeDb.getPassword(), //{noop} :意思是密码不加密,使用明文
                true, //enabled : 用户是否启用
                true, //accountNonExpired : 用户是否过期
                true, //credentialsNonExpired : 用户凭证是否过期 
                true, //accountNonLocked: 用户是否被锁定
                authorities  //权限集合,这里使用空集合
                );
        return userDetails;
    }
}

2、SecurityConfig配置

需要两个步骤,

第一步: 通过注解注入MyUserDetailsServiceService

第二步:在身份管理器配置方法里面将userDetailsService换成我们自己写MyUserDetailsServiceService


@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyUserDetailsServiceService myUserDetailsServiceService;
    /**
     * 身份安全管理器
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
        auth.userDetailsService(myUserDetailsServiceService);
    }
}

三、密码加密认证

​ 在”基于数据库完成用户认证”中我们所使用的密码都是明文的,规则是通过对密码添加 ’{noop}‘前缀。那么下面将对spring security 中的密码编码进行一下研究。

​ Spring Security 中 PasswordEncoder 就是我们对密码进行编码的工具接口。此接口只有两个功能:1. 匹配验证 2. 密码编码

​ 我们可以看到PasswordEncorder接口有众多实现类。其中NoOpPasswordEncoder是类是使用明文。而安全性最高,最常用的是BCryptPasswordEncoder加密类。

​ bcrypt是通过强哈希进行加密,每次加密的结果也不相同。它的安全性要高于MD5,当然,加密算法越安全,时常就越长。但是用户是感觉不到的。

​ 那么如何将我们上一节完成的基于明文密码的验证转化为bcrpt的方式呢。其实很简单,只需要将password的前缀改成{bcrypt}即可。可能你也会好奇,如果想用其他的加密方式,那它对应的前缀该去哪里找呢。只需要全局搜索PasswordEncoderFactories这个工厂类,我们就能看到所以的前缀名。

四、获取当权登录用户

在传统的web系统中,我们将登录成功的用户存入session中,在需要的时候从session中获取用户,但是在Spring Security中我们如何获取当前已经登录的用户呢?

方法一:

  • SecurityContextHolder

    他保留了iong的安全上下文SecurityContext,它计入了当前使用系统的用户信息。

  • SecurityContext

    安全上下文,获取当前经过身份验证的主题和身份验证的请求令牌。

代码实现

@GetMapping("/loginUser1")
public UserDetails getCurrentUser() {
        UserDetails principal = (UserDetails) SecurityContextHolder
            .getContext()  //获取上下文
            .getAuthentication() //获取认证信息
            .getPrincipal(); //获取当前用户登录信息
        return principal;
}

查询结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iLfFSMPf-1673327903117)(Spring sercrity.assets/image-20221224132542826.png)]

方法二:

通过在控制层方法中注入Authentication(认证信息)来获取Principal。

代码实现:

	@GetMapping("loginUser2")
    public UserDetails getCurrentUser(Authentication authentication){
        UserDetails principal = (UserDetails) authentication
                .getPrincipal();
        return principal;
    }

调用结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1VZNwbdf-1673327903118)(Spring sercrity.assets/image-20221224132616271.png)]

方法三:

在控制层形参中注入userDetails用户登录信息,但是需要加入注解才能注入。

代码实现:

 @GetMapping("/loginUser3")
    public UserDetails getCurrentUser(@AuthenticationPrincipal UserDetails userDetails) {
        return userDetails;
    }

调用结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TLHcwBLH-1673327903118)(Spring sercrity.assets/image-20221224132507798.png)]

五、记住我功能实现

​ 在很多的网站中,都会有RememberMe这个功能,这方便了用户在下一次登录时直接登录,避免了重复输入账户密码。Spring

Security针对于这个功能也帮助我们实现了。

方式一:使用token的方式

使用token的方式比较简单,他是通过将令牌存入到Cookie中。

原理图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u1d52QPn-1673327903119)(Spring sercrity.assets/image-20221224134741270.png)]

Token=MD5(username+分隔符+expiryTime+分隔符+password)

注意:这种方式不推荐使用,有严格是安全问题,密码保存在前端浏览器cookie中,容易被盗取破解。

实现:

   http    		.formLogin()
                .loginPage("/backend/login.html")  //自定义登录页面
                .loginProcessingUrl("/login")  //自定义登录页面所提交的路径
                .defaultSuccessUrl("/backend/index.html",true)
                .passwordParameter("password")
                .usernameParameter("username")
       
                //在这里配置rememberMe
       			.and().rememberMe() //开去记住我功能
                .tokenValiditySeconds(1209600) //token 失效时间,默认是两周
                .rememberMeParameter("remember-me") //前端提交请求参数 value可选 true yes 1 on 都可以
               
       			.and()
                .authorizeRequests().antMatchers("/backend/login.html").permitAll() //放行登录页面
                .anyRequest().authenticated();

前台代码

 <div>
        <input type="checkbox" name="remember-me" value="true">记住我
 </div>

方法二:持久化Token生成的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Jgvqtsm-1673327903119)(Spring sercrity.assets/image-20221224144214754.png)]

存入数据可的Token包含:

token:每次访问都会重新生成

series:登录序列号,用户输入用户名与密码登陆时,该值都会重新生成。使用remember-me功能,该值保持不变。

expiryTime : token 过期时间。

CookieValue=encode(series+token)

实现:

1.在securityConfig配置类中创建方法

 @Autowired
    DataSource dataSource;
    @Bean
    public PersistentTokenRepository getPersistentTokenRepository(){
        //1.创建一个PersistentTokenRepository的实现类
        JdbcTokenRepositoryImpl jdbcTokenRepository =new JdbcTokenRepositoryImpl();
        //2.设置数据源
        jdbcTokenRepository.setDataSource(dataSource);
        //3.启动时自动创建异常表, 第一次启动设置为 true 第二次启动设置为 false或者注释掉
        jdbcTokenRepository.setCreateTableOnStartup(true);
        //返回
        return jdbcTokenRepository;
    }

2.配置类

 http    .formLogin()
                .loginPage("/backend/login.html")
                .loginProcessingUrl("/login")
                .defaultSuccessUrl("/backend/index.html",true) 
                .passwordParameter("password")
                .usernameParameter("username")
                .and().rememberMe()
                .tokenValiditySeconds(1209600)
                .rememberMeParameter("remember-me")
               
     			//设置tokenRepository
     			.tokenRepository(getPersistentTokenRepository())
                
     			.and()
                .authorizeRequests().antMatchers("/backend/login.html").permitAll()
                .anyRequest().authenticated();

将Token存入到前台浏览器容易被盗取。那么如何避免这个问题那。我们在重要的aip上加一层限制,让与remember-me方式登录的用户需要进一步认证。例如百度云网盘在删除文件时的验证。

代码:

//获取认证的信息
  Authentication authentication =SecurityContextHolder.getContext().getAuthentication();
//如果返回true这说明登录认证信息来源于自动登录
boolean flag = RememberMeAuthenticationToken.class.isAssignableFrom(authentication.getClass());
if(flag){
    //如果时自动登录则将抛出此异常,spring security框架将会捕获并跳转到登录页面重新登录
    throw new RememberMeAuthenticationException("认证来源于RememberMe");
}

六、自定义登录成功异常处理类

​ 在某些场景下,用户登录成功或失败的情况下用户需要执行一些后续操作,比如登录日志的搜集,或者在前后端分类项目中,无论用户登录成功和失败后需要给前台页面返回对应的错误信息,有前台主导登录成功或失败的页面跳转,这个时候需要要用到AuthentiactionSuccessHandler与Authentication Failure Handler。

自定义成功处理:

​ 实现SuthenticationSuccessHandler接口,并重写onAuthenticationSuccess()方法。

自定义失败处理:

​ 实现SuthenticationFailureHandler接口,并重写onAuthenticationFailure()方法。

代码实现:

  1. 创建一个Service并继承成功与失败相对应的接口,并实现方法

    
    
    /**
     * @author lihaojie
     * @date 2022/12/25 11:27
     **/
    @Service
    public class MyAuthenticationService 
        implements AuthenticationFailureHandler, AuthenticationSuccessHandler {
        /**
         * 登录成功处理类
         *
         * @param httpServletRequest
         * @param httpServletResponse
         * @param authentication
         */
        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            //登录成功后跳转到前端
            //可以记录一下ip 等用户数据
            System.out.println("登录成功");
        }
        /**
         * 登录失败处理类
         *
         * @param httpServletRequest 
         * @param httpServletResponse
         * @param e
         */
        @Override
        public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
            System.out.println("登录失败");
        }
    }
    
    
  2. 将自定的Service注入到SecurityConfig配置类并配置

    /**
     * 在spring security 配置类中
     **/
        @Autowired
        private MyAuthenticationService myAuthenticationService;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    
            http    .formLogin() //开启表单
                	//配置自定义的登录后的处理类
                    .successHandler(myAuthenticationService)
                    .failureHandler(myAuthenticationService) 
    
        }
    
    
    

七、Session管理

​ Spring Security可以与Spring Session库配合使用,只需要做一些简单的配置就可以实现一些功能,例如:会话过期,一个账号只能同时在线一个,集群session等。

1.会话超时

(1)配置session会话超时时间,默认为30分钟,但是Spring Boot中会话超时时间至少60秒

#session设置
#配置session超时时间
server.servlet.session.timeout=60

当session超时后,默认跳转到登录页面

(2)自定义session超时后的地址

http.sessionManagement()//设置session管理
    			.invalidSessionUrl("/toLoginPage") //session无效后跳转的路径,默认是登陆页面

2.并发控制

​ 并发控制即同一个账号在线个数,同一个账号在线个数如果设置为1,这表示该账号在同一时间内只能有一个有效登录,如果同意账号又在其他地方登录,那么就将上次登录的会话过期,即后面的登录会踢掉前面的登录。

(1)设置最大会话数量

 http.sessionManagement().invalidSessionUrl("/toLoginPage") //当session超时的跳转路径
                                .maximumSessions(1)			//设置session最大数量
                                .expiredUrl("/toLoginPage"); //session过期(被踢掉)的跳转路径

(2)设置最大会话数量

 http.sessionManagement().invalidSessionUrl("/toLoginPage") //当session超时的跳转路径
                                .maximumSessions(1)			//设置session最大数量
                                .maxSessionsPreventsLogin(true) //当打到最大数量后将阻止新的登录,
     															//这意味着expiredUrl将失效
                                .expiredUrl("/toLoginPage"); //session过期(被踢掉)的跳转路径

3.基于redis的session共享

​ 在分布式的架构中,我们通过Nginx实现自己的服务代理,那么再一次登录后可能会跨集群调用,而每跨一次都需要用户登录一次。那如何解决此问题?可以通过redis做session共享,Spring Security在做获取session时都会从redis中获取。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q0yjIB5E-1673327903120)(Spring sercrity.assets/image-20221225184305804.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t3GyxniU-1673327903121)(Spring sercrity.assets/image-20221225184325172.png)]

(1)引入依赖

  <!--基于redis实现session共享-->
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

(2)设置session储存类型

#使用redis共享session
spring.session.store-type=redis

八、csrf防护机制

1.csrf介绍

(1)csrf简介

​ CSFF(Cross-site request forgery) ,中文名称:跨站请求伪造。你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求,CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账…造成的问题包括:个人隐私泄露以及财产安全。CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到06年才开始被关注,08年,国内外的多个大型社区和交网站分别爆出CSRF漏洞,如: NYTime.om(纽约时报)、Metafilter (一个大型的BLG网站),Youube和度Hl…而现在,互联网上的许多站点仍对此毫无防备,以至于安全业界称CSRF为”沉睡的巨人”。

​ (2)csrf原理图

从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成三个步骤:
1.登录受信任网站A,并在本地生成Cookie。
2.在不登出A的情况下,访问危险网站B。
3.触发网站B中的一些元素

(3) CSRF的防御策略

​ 在业界目前防御CSRF 攻击主要有三种策略:验证 HTTP Referer 字段,在请求地址中添加oken 并验证,在 HTTP 头中自定义属性并验证。
①验证HTTP Referer字段
根据 HTTP 协议,在 HTTP头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址,在通常情况下,访问一个安全受限页面的请求来自于同一个网站,在后台请求验证其 Referer 值,如果是以自身安全网站开头的域名,则说明该请求是是合法的。如果Referer 是其他网站的话,则有可能是黑客的 CSRF 攻击,拒绝该请求。
②在请求地址中添加 token 并验证
​ CSRF攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于cookie 中,因此里客可以在不知道这些验证信息的情况下直接利用用户自己的cookie 来通过安全验证,要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦载器来验证这个token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求3在HTTP头中自定义属性并验证。这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。

2.Spring Security实现csrf

​ Spring Security默认开始csrf,并对所有post起作用。

(1)前端在提交时需要携带token

<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>记住我

(2)配置忽略的路径

http.csrf().ignoringAntMatchers("/url")

九、跨域与CORS

​ 跨域,实质上是浏览器的一种保护处理,如果产生了跨域,服务器在返回结果时就会被浏览器拦截(注意:此时请求是可以正常发起的,只是浏览器对其进行了拦截),导致响应的内容不可用.产生跨域的几种情况有一下:

  • 请求域名发生改变(主域名,副域名任意一个改变都可)
  • 请求协议发生改变(http --> https)
  • 请求端口发生改变(8080 -->8090)

1.解决跨域

1.JSONP
浏览器允许一些带src属性的标签跨域,也就是在某些标签的src属性上写ur地址是不会产生跨域问题2.CORS解决跨域
CORS是一个W3C标准,全称是"跨域资源共享”(Cross-origin resoure sharing) CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,E浏览器不能低于IE10。浏览器在发起真正的请求之前,会发起一个OPTIONS类型的预检请求,用于请求服务器是否允许跨域,在得到许可的情况下才会发起请求

/**
*跨城配置信息源
*/
public CorsConfiqurationSource corsConfigurationSource() f
CorsConfiguration corsConfiguration = new CorsConfiguration();
//允许跨域的站点
corsConfiguration.addAllowedOrigin(*");
//允许跨域的http方法
corsConfiguration.addAllowedMethod("*");
// 允许跨域的请求头
corsConfiquration.addAllowedHeader("*);
// 允许带凭证
corsConfiguration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
// 对所有ur1都生效
urlBasedCorsConfigurationSource.registerCorsConfiguration( path: "/**n, corsConfiguration);
return urlBasedCorsConfigurationSource;
//开启跨域支持
http.cors().confiqurationSource(corsConfiqurationSource());

授权

一、内置权限表达式

​ Spring Security 使用Spring EL来支持,主要用于Web访问和方法安全上,可以通过表达式来判断是否具有访问权限.下面是SpringSecurity常用的内置表达式.ExpressionUrlAuthorizationConfigurer定义了所有的表达式:

表达式说明
permitAll指定任何人都允许访问。
denyAll指定任何人都不允许访问
anonymous指定匿名用户允许访问。
rememberMe指定已记住的用户允许访问。
authenticated指定任何经过身份验证的用户都允许访问,不包含anonymous
fullyAuthenticated指定由经过身份验证的用户允许访问,不包含anonymous和rememberMe
hasRole(role)指定需要特定的角色的用户允许访问,会自动在角色前面插入ROLE_“
hasAnvRole([role1,role2])指指定需要任意一个角色的用户允许访问,会自动在角色前面插入ROLE_“
hasAuthority(authority)指定需要特定的权限的用户允许访问
hasAnyAuthority([authority,authority])指定需要任意一个权限的用户允许访问
haslpAddress(ip)指定需要特定的IP地址可以访问

二、基于URL的权限控制

1.配置类

//开启权限控制
http.authorizeRequests().antMatchers("/employee/**")   //对"/employee/**"开启权限控制
    .hasRole("ADMIN");  //指定特定用户(ADMIN)访问
http.authorizeRequests().antMatchers("/product/**")    //对”/product/**”开启权限控制
    .access("hasAnyRole('ADMIN','PRODUCT') and hasIpAddress('127.0.0.1')");  //对于多个限定可以使用el表达式
http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler)//myAccessDeniedHandler自定义权限不足处理类

2.myAccessDeniedHandler自定义权限不足处理类


/**
 * 自定义权限不足处理类
 **/
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        httpServletResponse.setContentType("text/html;charset=UTF-8");
        httpServletResponse.getWriter().write("权限不足");
    }
}

3.赋予用户权限

/**
 * 使用数据库账号登录
 **/
@Service
public class MyUserDetailsServiceService implements UserDetailsService {
    @Autowired
    private EmployeeService employeeService;
    /**
     * 设置为从数据库获取账户与密码
     *
     * 我们通过用户名查询不到用户,则抛出异常,如果查到了用户,我们需要将查询到丢username与password等一些用户状态的参数进行封装,封装为一个UserDetails
     * 返回给框架,框架会自己将数据库的用户密码与前台传入的做对比
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        Employee employeeDb=employeeService.findByUsername(username);
        if(employeeDb==null){
            throw new UsernameNotFoundException("用户没找到:"+username);
        }
        //创建权限集合
        Collection<GrantedAuthority> authorities =new ArrayList<>();
        //赋予权限 赋予权限时要加REOLE_前缀,配置时不用(系统给我们加)
        if(username.equalsIgnoreCase("admin")){
            authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        }else {
            authorities.add(new SimpleGrantedAuthority("ROLE_OTHER"));
        }
 		//创建UserDetails用户详情对象并返回给框架
        UserDetails userDetails = new org.springframework.security.core.userdetails.User
                (username,
                "{bcrypt}"+ employeeDb.getPassword(),
                authorities); //用户权限集合

        return userDetails;
    }
}

三、方法粒度的权限控制

​ 针对方法粒度的访问控制比较复杂,Spring Security 提供了4中注解,其中Pre开头的为方法调用前的控制,Post开头的为方法调用后,对返回值的控制。在使用注解进行方法权限控制时,我们需要开启方法权限控制:

// spring security 配置类

@EnableGlobalMethodSecurity(prePostEnabled = true)  //开启方法权限控制
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

}

1. @PreAuthorize() 注解

​ 在方法调用之前,对方法权限进行控制。

​ (1)针对方法参数控制

 @PreAuthorize("#id<3")  //Controller方法参数控制,id<3的才能被访问

​ (2)对用户角色控制

@PreAuthorize("hasRole('ADMIN')")  //调用Controller方法的角色权限控制,只有ADMIN用户才能访问

注意:这里算是设置权限控制角色,所以不加前缀 “ROLE_”

2. @PostAuthorize() 注解

​ 注解适合有返回值的方法权限校验。

//对返回值进行权限校验,只能自己查询自己的信息
@PostAuthorize("returnObject.username==authentication.principal.username")

returnObject : 为方法返回的结果对象。

authentication : 身份验证信息

3. @PreFilter()注解

​ 在方法调用前,对于集合类的参数进行过滤,过滤掉不符合的数据,保留符合的数据。

//过滤掉ids集合中,id不等于0的
@PreFilter(filterTarget = "ids",value = "filterObject==0")

filterTarget : 要过滤的目标参数,一般时集合

filterObject :为filterTarget 中的一个元素

4. @PostFilter()注解

​ 在方法调用后,对方法的结果进行过滤。

//提出所有id为奇数的结果
@PostFilter("fileterObject.id%2==0")

ending……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值