spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403

摘要:
@如果PreAuthorize具有多个权限设置,请使用following://You必须具有访问@PreAuthorize的所有权限//至少有一个可以访问@PreAuthorize密钥,超级密钥!!!!我已经被愚弄了很长时间:@PreAuthorize权限名称必须以ROLE_为前缀。请随意编写以下内容,如ROLE_admin、ROLE_user、ROLE_kk22、ROLE_否则,love无法正确识别权限,因为hasRole定义了角色,前缀ROLE_将自动添加到权限中,因此,它不太常用。如果使用@PreAuthorize,则无需添加前缀。这是怎么一回事?您不需要添加前缀。因为hasAuthority定义了权限,所以只需写下名称即可。
1.前言

以前学习的时候使用权限的拦截,一般都是对路径进行拦截 ,要么用拦截器设置拦截信息,要么是在配置文件内设置拦截信息,

spring security 支持使用注解的形式 ,写在方法和接口上拦截 ,

分别支持 三种  :

@PreAuthorize("hasRole('ROLE_xxx')" )   

@PostAuthorize("returnObject.type == authentication.name")  

@Secured({ "ROLE_DBA", "ROLE_ADMIN" })

常用的是 @PreAuthorize  和  @Secured  ,但是推荐使用  @PreAuthorize 因为支持Spring EL表达式 。

@PreAuthorize如果有多个权限设置,则使用如下:

  //必须有全部的权限才可以访问
    @PreAuthorize("hasRole('ROLE_admin') and hasAnyRole('ROLE_user')")
    //至少有一个即可访问
    @PreAuthorize("hasRole('ROLE_admin') or hasAnyRole('ROLE_user')")
重点 ,超级重点 !!!!坑了我好长时间:

使用 @PreAuthorize("hasRole('ROLE_admin'))

权限名字必须带前缀 ROLE_ 后面的随便写 比如 ROLE_admin , ROLE_user , ROLE_kk22 , ROLE_love 否则无法正确识别权限
因为hasRole 就是定义角色的,转成权限会自动添加前缀ROLE_ ,因此,不怎么使用。
如果使用
@PreAuthorize("hasAuthority('admin')") 则不需要带前缀,是什么就是什么,不需要添加前缀,
因为 hasAuthority 就是定义权限的,写入名字即可。
 
经过测试:
 
    路径拦截权限的名称必须与权限列表注册的一样,经过测试,使用hasRole的注解, 方法级别的注解权限需要
ROLE_前缀 ,因此,路径拦截权限的名称、注解权限名称、数据库存储的权限名称都要加
ROLE_前缀最好,避免出现错误, 如果数据库的权限名称不加ROLE_前缀,那么在注册权
限列表的时候记得拼接ROLE_前缀。
如果不想麻烦,可以使用
hasAuthority ,不需要添加前缀,
不论是那种方法,注解的限制效果和在security配置文件设置的效果是一样的。
 
2.操作

(1)准备一个建好的spring boot + spring security 工程 【具体操作这里不解释 ,我的其他随笔有具体记载】

目录结构

spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第1张

在security 配置类 开启 权限注解功能

@EnableGlobalMethodSecurity(prePostEnabled = true)

spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第2张

 注解 @EnableGlobalMethodSecurity 有3个参数,【默认是false  ,需要开启时设为true】

prePostEnabled :确定 Spring Security 前置注释 [@PreAuthorize,@PostAuthorize,..] 是否应该启用;
 secureEnabled : 确定 Spring Security 安全注释 [@Secured] 是否应该启用;
 jsr250Enabled : 确定 JSR-250注释 [@RolesAllowed..] 是否应该启用;

完整的配置类

spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第3张spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第4张
package com.example.security5500.securityConfig;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;

//这个加不加无所谓
@Configuration
//开启security自定义配置
@EnableWebSecurity
//开启 Controller层的访问方法权限,与注解@PreAuthorize("hasRole('ROLE_admin')")配合,会拦截注解了@PreAuthrize注解的配置
// 想要@PreAuthorize正确执行 ,权限关键字必须带前缀 ROLE_  ,后面的部分可以随便写!!!!靠,琢磨了4小时了 ,终于找到原因了
@EnableGlobalMethodSecurity(prePostEnabled = true)
//, securedEnabled = true
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    //实例自定义登录校验接口 【内部有 数据库查询】
    @Autowired
    private DbUserDetailsService dbUserDetailsService;

    //忽略拦截的静态文件路径
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .antMatchers(
                        "/js/**",
                        "/css/**",
                        "/img/**",
                        "/webjars/**");
    }

    //拦截规则设置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //允许基于使用HttpServletRequest限制访问
                //即授权请求设置
                .authorizeRequests()
                //设置不拦截页面,可直接通过,路径访问 "/", "/index",  则不拦截,
                .antMatchers("/", "/index", "/hhk/**")
                //是允许所有的意思
                .permitAll()
//                //访问 /hai 需要admin权限 ,无权限则提示 403
//                .antMatchers("/hai").hasAuthority("admin")
//                //访问 /kk 需要admin或user权限 ,无权限则提示 403
//                .antMatchers("/kk").hasAnyAuthority("admin", "user")
//                //路径/admin/**所有的请求都需要admin权限 ,无权限则提示 403
//                .antMatchers("/admin/**").hasAuthority("admin")
                //其他页面都要拦截,【需要在最后设置这个】
                .anyRequest().authenticated()
                .and()
                //设置自定义登录页面
                //即开启登录设置
                .formLogin()
                //指定自定义登录页面的访问虚拟路径
                .loginPage("/login")
                .permitAll()
                .and()
//        添加退出登录支持。当使用WebSecurityConfigurerAdapter时,这将自动应用。默认情况是,访问URL”/ logout”,使HTTP Session无效
//        来清除用户,清除已配置的任何#rememberMe()身份验证,清除SecurityContextHolder,然后重定向到”/login?success”
                //即开启登出设置
                .logout()
//                //指定的登出操作的虚拟路径,需要以post方式请求这个 http://localhost:5500/mylogout 才可以登出 ,也可以直接清除用户认证信息达到登出目的
//                .logoutUrl("/mylogout")
                //使httpsession失效
                .invalidateHttpSession(true)
                //清除认证信息
                .clearAuthentication(true)
                //登出请求匹配器,新建一个蚂蚁路径请求匹配器 ,与 .logoutUrl("/mylogout")效果一样
                .logoutRequestMatcher(new AntPathRequestMatcher("/mylogout"))
                //登出成功后访问的地址
                .logoutSuccessUrl("/home")
                .permitAll()
                .and()
                //开启记住我设置,用于自动登录
                .rememberMe()
                //密钥
                .key("unique-and-secret")
                //存在cookie的用户名[用于cookie名]
                .rememberMeCookieName("remember-me-cookie-name")
                //生命周期,单位毫秒
                .tokenValiditySeconds(24 * 60 * 60);
        //登陆后"选择记住我" ,会生成cookie  ,登出则会自动删除该cookie  , 只要不登出且未超出生命周期 ,那么关闭浏览器后再次访问将自动登录
        //  [name]                          [value]                                                     [domain]   [path]      [expires/max-age]     [size]   [httponly]   [priority]
        //remember-me-cookie-name    eGk6MTU5MTIwODAzNDk5MTozZWUyN2FlMmEwMWQxNDczMDhhY2ZkYTAxZWQ5ZWQ5YQ    localhost    /       2020-06-03T18:13:54.992Z    89       ✓            Medium


    }


    /**
     * 添加 UserDetailsService, 实现自定义登录校验,数据库查询
     */
    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
        //注入用户信息,每次登录都会来这查询一次信息,因此不建议每次都向mysql查询,应该使用redis
        //密码加密
        builder.userDetailsService(dbUserDetailsService);
//                .passwordEncoder(passwordEncoder());
    }

    /**
     * BCryptPasswordEncoder相关知识:
     * 用户表的密码通常使用MD5等不可逆算法加密后存储,为防止彩虹表破解更会先使用一个特定的字符串(如域名)加密,然后再使用一个随机的salt(盐值)加密。
     * 特定字符串是程序代码中固定的,salt是每个密码单独随机,一般给用户表加一个字段单独存储,比较麻烦。
     * BCrypt算法将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理salt问题。
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


//    /**
//     * 选择加密方式 ,密码不加密的时候选择 NoOpPasswordEncoder,不可缺少,否则报错
//     * java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
//     */
//    @Bean
//    public static PasswordEncoder passwordEncoder() {
//        return NoOpPasswordEncoder.getInstance();
//    }
    
}
View Code

(2)在需要权限拦截的方法上加入注解 @PreAuthorize("hasRole('ROLE_xxx')"  )   

有多个权限则使用

@PreAuthorize("hasRole('ROLE_xxx')  AND  hasRole('ROLE_xxx2')")   

[必须具备两个权限才可以]

@PreAuthorize("hasRole('ROLE_xxx')  or  hasRole('ROLE_xxx2')")   

【只要有其中一个权限即可】

spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第5张

@PreAuthorize("hasRole('ROLE_love1')")

意思是需要权限 ROLE_love1才可以访问这个方法 ,

(3)那么数据库中的权限字符串该怎么写?

spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第6张

 图中是我测试的权限名称, 以 符号 ,来分隔多个权限  ,经过测试 ,只有ROLE_前缀的权限才可以被注解识别 【原因不清楚,找不到相关资料】

因此,不论是注解还是数据库存储的值,都需要加 ROLE_前缀 ,

//

当然 ,数据库中也可以不加,可以在注册权限到内存的时候再加 ,

但是容易遗忘等原因出错,因此不建议使用,如下图写法:

spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第7张

3.测试

(1)启动工程 ,端口5500

访问网址 http://localhost:5500/home

被拦截需要登录

使用一个 无 ROLE_love1 权限 的账户登录

username = xi

password = 11

spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第8张

 点击登录显示403

spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第9张

控制台打印权限

spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第10张

 (2)

换一个有ROLE_love1 权限 的账户登录

username = cen

password = 11

【因为我在配置类配置类登录页面路径

spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第11张

 因此想要重新登录,需要访问 网址 http://localhost:5500/login  返回到登录页面  ,

注意 ,访问登录页面不会自动登出,但是 进入登录页面后,提交以此表单登录申请 后将自动登出 原本已经登录的账户 ,即便登录失败 ,照样会登出

spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第12张

  点击登录,成功进入页面

spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第13张

 控制台打印权限

spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403第14张

------------------------------

参考博文原址 : https://blog.csdn.net/HiBoyljw/article/details/84032839

免责声明:文章转载自《spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Sqlite(数据库)浅谈单工,半双工和全双工有何区别和联系?下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

django后台admin的配置

DJANGO ADMIN 一些有用的设置Django自带的后台管理是Django明显特色之一,可以让我们快速便捷管理数据。后台管理可以在各个app的admin.py文件中进行控制。以下是我最近摸索总结出比较实用的配置。若你有什么比较好的配置,欢迎补充。 一、基本设置 1、应用注册 若要把app应用显示在后台管理中,需要在admin.py中注册。这...

内网穿透java版

内网穿透 1 借鉴 https://blog.csdn.net/today_08/article/details/99965189 https://github.com/Pluto-Whong/natcross.git 不再维护了,建议使用natcross2https://github.com/Pluto-Whong/natcross-boot.githt...

shiro配置

SpringMVC整合Shiro, Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能。 第一步:配置web.xml ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <!-- 配置Shiro过滤器,先让Shiro过滤系统接收到的请求 -->   <!-...

海康、大华IPC的rtsp格式

海康:  rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream说明:username: 用户名。例如admin。password: 密码。例如12345。ip: 为设备IP。例如 192.0.0.64。port: 端口号默认为554,若为默认可不填写。c...

Win7下的内置FTP组件的设置详解

在局域网中共享文件,FTP是比较方便的方案之一。Win7内部集成了FTP,只是设置起来颇费一番功夫。着文以记之。 一、安装FTP组件 由于Win7默认没有安装FTP组件。故FTP的设置第一步就是安装FTP组件 点击:控制面板—》程序和功能—》打开或关闭Windows功能。勾选“FTP服务器”及“FTP服务”“FTP扩展性”,点击“确定”,安装FTP组件。如...

k8s 证书创建

一 证书分类 服务器证书: server cert :用户客户端验证服务端的身份。 客户端证书: client cert:用户服务端验证客户端的身份。 对等证书: peer cert: 用户成员之间的身份验证,例如etcd,(该证书,它即是server cert,又是client cert)。   二 k8s集群证书的分类 ETCD: 需要server c...