前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Security 权限控制

Security 权限控制

作者头像
用户9184480
发布2024-12-13 14:42:37
发布2024-12-13 14:42:37
7700
代码可运行
举报
文章被收录于专栏:云计算linux云计算linux
运行总次数:0
代码可运行

Security 权限控制

一、注解权限控制

1.1 启用注解配置

@EnableGlobalMethodSecurity分别有prePostEnabled、securedEnabled、jsr250Enabled三个字段,其中每个字段代码一种注解支持,默认为false,true为开启

在同一个应用程序中,可以启用多个类型的注解,但是只应该设置一个注解对于行为类的接口或者类

代码语言:javascript
代码运行次数:0
复制
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ....
}

1.2 设置用户角色权限

Security中角色和权限设置保存的方式一样,但是使用方式不一样

角色

管理员/测试人员/普通用户

对应使用的代码是hasRole,授权代码需要加ROLE_前缀,controller上使用时根据需要可以不要加前缀

权限

执行的操作:路径访问权限 功能操作权限

对应使用的代码是hasAuthority,设置和使用时,名称保持一至即可

代码语言:javascript
代码运行次数:0
复制
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    if(this.cname.equals("zs")){
        return AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN,add,select");
    }else{
        return AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_TEST,select");
    }
}

实际项目中,用户角色权限信息从数据库中获取

1.3 获取用户信息

代码语言:javascript
代码运行次数:0
复制
	@RequestMapping("/user1")
    @ResponseBody
    public Object user1(){
        return SecurityContextHolder.getContext().getAuthentication();
    }

    @RequestMapping("/user2")
    @ResponseBody
    public Object user2(HttpServletRequest request){
        return request.getUserPrincipal();
    }

    @RequestMapping("/user3")
    @ResponseBody
    public Object user3(Principal principal){
        return principal;
    }

    @RequestMapping("/user4")
    @ResponseBody
    public Object user4(Authentication authentication){
        return authentication;
    }

1.4 在方法上添加注解

securedEnabled

安全注解

使用@Secured注解

@Secured注解是用来定义业务方法的安全配置。在需要安全的方法上指定 @Secured,并且只有那些角色/权限的用户才可以调用该方法。

@Secured缺点就是不支持Spring EL表达式。不够灵活。并且指定的角色必须以ROLE_开头,不可省略。

代码语言:javascript
代码运行次数:0
复制
	@Secured("ROLE_ADMIN")
    @RequestMapping("/m1")
    @ResponseBody
    public String m1(){
        return "m1";
    }

    @Secured({"ROLE_TEST"})
    @RequestMapping("/m2")
    @ResponseBody
    public String m2(){
        return "m2";
    }

    @Secured({"ROLE_ADMIN","ROLE_TEST"})
    @RequestMapping("/m3")
    @ResponseBody
    public String m3(){
        return "m3";
    }
jsr250Enabled

JSR-250注解

比较简单,在指定方法中使用方法注解@RolesAllowed,@DenyAll,@PermitAll

  • @DenyAll 全部拒绝
  • @PermitAll 全部通过
  • @RolesAllowed({"USER", "ADMIN"})
    • 代表标注的方法只要具有USER, ADMIN任意一种权限就可以访问。这里可以省略前缀ROLE_,实际的权限可能是ROLE_ADMIN。
代码语言:javascript
代码运行次数:0
复制
    // 全部拒绝访问
    @DenyAll
    @RequestMapping("/m4")
    @ResponseBody
    public String m4(){
        return "m4";
    }

    // 可以访问
    @PermitAll
    @RequestMapping("/m5")
    @ResponseBody
    public String m5(){
        return "m5";
    }

    // 拥有角色可访问
    @RolesAllowed({"ROLE_ADMIN","ROLE_TEST"})
    @RequestMapping("/m6")
    @ResponseBody
    public String m6(){
        return "m6";
    }

    // 可省略ROLE_
    @RolesAllowed({"ADMIN","TEST"})
    @RequestMapping("/m7")
    @ResponseBody
    public String m7(){
        return "m7";
    }
prePostEnabled

前置注解

该注解更适合方法级的安全,也支持Spring 表达式语言,提供了基于表达式的访问控制

  • @PreAuthorize --适合进入方法之前验证授权
  • @PostAuthorize --检查授权方法之后才被执行
  • @PostFilter --在方法执行之后执行,而且这里可以调用方法的返回值,然后对返回值进行过滤或处理或修改并返回
  • @PreFilter --在方法执行之前执行,而且这里可以调用方法的参数,然后对参数值进行过滤或处理或修改
代码语言:javascript
代码运行次数:0
复制
@PreAuthorize("authentication.principal.username == 'tom' ")
@PreAuthorize("hasRole('ROLE_ADMIN')")

// 只能user角色可以访问
@PreAuthorize ("hasAnyRole('user')")

// user 角色或者 admin 角色都可访问
@PreAuthorize ("hasAnyRole('user') or hasAnyRole('admin')")

// 同时拥有 user 和 admin 角色才能访问
@PreAuthorize ("hasAnyRole('user') and hasAnyRole('admin')")

// 限制只能 id 小于 10 的用户
@PreAuthorize("#id < 10")
User findById(int id);

// 只能查询自己的信息
@PreAuthorize("principal.username.equals(#username)")
User find(String username);

// 限制只能新增用户名称为abc的用户
@PreAuthorize("#user.name.equals('abc')")
void add(User user)
    
// 查询到用户信息后,再验证用户名是否和登录用户名一致
@PostAuthorize("returnObject.name == authentication.name")
@GetMapping("/get-user")
public User getUser(String name){
    return userService.getUser(name);
}
// 验证返回的数是否是偶数
@PostAuthorize("returnObject % 2 == 0")
public Integer test(){
    // ...
    return id;
}

// 指定过滤的参数,过滤偶数
@PreFilter(filterTarget="ids", value="filterObject%2==0")
public void delete(List<Integer> ids, List<String> username)
    
@PostFilter("filterObject.id%2==0")
public List<User> findAll(){
    ...
    return userList;
}

表达

描述

hasRole([role])

返回true当前委托人是否具有指定角色。默认情况下,如果提供的角色不是以“ ROLE_”开头,则会添加该角色。可以通过修改defaultRolePrefixon来自定义DefaultWebSecurityExpressionHandler。

hasAnyRole([role1,role2])

返回true当前委托人是否具有提供的任何角色(以逗号分隔的字符串列表形式)。默认情况下,如果提供的角色不是以“ ROLE_”开头,则会添加该角色。可以通过修改defaultRolePrefixon来自定义DefaultWebSecurityExpressionHandler。

hasAuthority([authority])

true如果当前主体具有指定的权限,则返回。

hasAnyAuthority([authority1,authority2])

返回true当前委托人是否具有提供的任何角色(以逗号分隔的字符串列表形式)

principal

允许直接访问代表当前用户的主体对象

authentication

允许直接访问Authentication从SecurityContext

permitAll

总是评估为 true

denyAll

总是评估为 false

isAnonymous()

返回true当前委托人是否为匿名用户

isRememberMe()

返回true当前主体是否是“记住我”的用户

isAuthenticated()

true如果用户不是匿名的,则返回

isFullyAuthenticated()

返回true如果用户不是匿名或记得,我的用户

hasPermission(Object target, Object permission)

返回true用户是否可以访问给定权限的给定目标。例如,hasPermission(domainObject, 'read')

hasPermission(Object targetId, String targetType, Object permission)

返回true用户是否可以访问给定权限的给定目标。例如,hasPermission(1, 'com.example.domain.Message', 'read')

二、RBAC权限控制

RBAC(role-Based-access control),一般都是由 3个部分组成,一个是用户,一个是角色 ,一个是资源(菜单,按钮),然后就是 用户和角色的关联表,角色和资源的关联表

核心就是判断当前的用户所拥有的URL是否和当前访问的URL是否匹配。

2.1 创建校验类

代码语言:javascript
代码运行次数:0
复制
@Component("rbacConfig")
public class RbacConfig {

    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        boolean hasPermission = false;
        
        Object principal = authentication.getPrincipal();// 获取登录用户的详细信息
        // 判断是否登录
        if (principal instanceof UserDetails) {
            // 类型转换
            MyUser myUser = (MyUser) principal;
			
            // 获取请求路径
            String uri = request.getRequestURI();
            
            // 获取用户权限并判断
            ......
        }
        
        return hasPermission;
    }
}

2.2 添加自定义的权限表达式

代码语言:javascript
代码运行次数:0
复制
.authorizeRequests()
.anyRequest()
.access("@rbacConfig.hasPermission(request,authentication)")

@rbacService 就是声明的bean对象

三、支持跨域请求

3.1 添加跨域请求配置

代码语言:javascript
代码运行次数:0
复制
@Configuration
public class GlobalCorsConfig {

    @Bean
    @Order("0")
    public CorsFilter corsFilter(){
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 允许请求带有验证信息
        corsConfiguration.setAllowCredentials(true);
        // 允许请求域名
        corsConfiguration.addAllowedOrigin("http://localhost:8081");
        // 允许请求头
        corsConfiguration.addAllowedHeader("*");
        // 允许请求方式
        corsConfiguration.setAllowedMethods(Arrays.asList("POST"));

        // 基于路径的苦于配置源
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
}

3.2 修改配置支持跨域请求

代码语言:javascript
代码运行次数:0
复制
				// 跨域资源共享
                .cors()
                .and()
                .csrf()
                .disable()
                .authorizeRequests()
                //处理跨域请求中的Preflight请求
                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()

3.3 修改VUE携带验证信息

中文文档

https://www.kancloud.cn/yunye/axios/234845/

封装axios

代码语言:javascript
代码运行次数:0
复制
import axios from 'axios'

let http = axios.create({
    baseURL:"http://localhost:8088",
    withCredentials:true,
    timeout:5000,
    responseType:'json'
})

// 添加请求拦截器
http.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});

// 添加响应拦截器
http.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
}, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
});

export default http

修改引入的axios

代码语言:javascript
代码运行次数:0
复制
import http from './utils/Http'

Vue.prototype.$axios = http

四、设置权限不足异常

4.1 添加异常处理类

代码语言:javascript
代码运行次数:0
复制
@Component
public class AccessExceptionHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        httpServletResponse.setCharacterEncoding("UTF-8");

        httpServletResponse.setContentType("application/json");

        httpServletResponse.getWriter().println("{\"code\":403,\"message\":\"没有权限访问呀!\",\"data\":\"\"}");

        httpServletResponse.getWriter().flush();

    }
}

4.2 配置异常处理

代码语言:javascript
代码运行次数:0
复制
			http...
                .and()
				.exceptionHandling()
                .authenticationEntryPoint(authenticationError)
                .accessDeniedHandler(accessExceptionHandler)

4.3 配置登录异常处理

判断是否是ajax请求,ajax请求返回json

代码语言:javascript
代码运行次数:0
复制
@Component
public class AuthenticationError implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        boolean isajax = false;

        String accept = httpServletRequest.getHeader("accept");
        if (accept != null && accept.indexOf("application/json") != -1)
        {
            isajax= true;
        }

        String xRequestedWith = httpServletRequest.getHeader("X-Requested-With");
        if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1)
        {
            isajax= true;
        }

        if(isajax){
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("application/json");
            httpServletResponse.getWriter().println("{\"code\":500,\"message\":\"用户名密码错误!\",\"data\":\"\"}");
            httpServletResponse.getWriter().flush();
        }else{
            httpServletResponse.sendRedirect("failure");
        }
    }
}

五、表结构

用户表

1001 zs 123...

角色表

R01 管理员 admin ....

用户角色关联表

1001 R01

模块表

P01 员工管理 /emp

角色模块表

R01 P01

R01 P02

功能表

G01 ADD

G02 SEELCT

角色功能关联表

R01 G01

R01 G02

(模块功能表)

六、前后端分离

前台访问后台接口获取数据信息,必须把登录用户的信息随着请求传递给后台接口

每一次请求响应都需要携带用户信息。

Token

后台:JWT

前台:VUEX,sessionstorage,localstrorage

权限控制

后台:注解,RBAC

前台:

  1. 保存用户的登录信息;
  2. 封装方法,判断用户是否有某个角色或者权限;

动态路由

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-03-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Security 权限控制
  • 一、注解权限控制
    • 1.1 启用注解配置
    • 1.2 设置用户角色权限
      • 角色
      • 权限
    • 1.3 获取用户信息
    • 1.4 在方法上添加注解
      • securedEnabled
      • jsr250Enabled
      • prePostEnabled
  • 二、RBAC权限控制
    • 2.1 创建校验类
    • 2.2 添加自定义的权限表达式
  • 三、支持跨域请求
    • 3.1 添加跨域请求配置
    • 3.2 修改配置支持跨域请求
    • 3.3 修改VUE携带验证信息
  • 四、设置权限不足异常
    • 4.1 添加异常处理类
    • 4.2 配置异常处理
    • 4.3 配置登录异常处理
  • 五、表结构
  • 六、前后端分离
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档