权限控制与安全认证 Spring Security(三)

本文涉及的产品
访问控制,不限时长
简介: 权限控制与安全认证 Spring Security(三)

3.3 编写查询权限方法

在认证后进行授权需要根据用户id查询到用户的权限,写法如下:

1、编写用户、角色、权限实体类

// 不要命名为User,避免和Spring Security提供的User混淆
@Data
public class Users {
  private Integer uid;
  private String username;
  private String password;
  private String phone;
}
// 角色
@Data
public class Role {
  private String rid;
  private String roleName;
  private String roleDesc;
}
// 权限
@Data
public class Permission {
  private String pid;
  private String permissionName;
  private String url;
}

2、编写UsersMapper接口

// 根据用户名查询权限
List<Permission> findPermissionByUsername(String username);

3、编写UsersMapper.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zj.mapper.UsersMapper">
    <select id="findPermissionByUsername" parameterType="string" resultType="com.zj.pojo.Permission">
           select DISTINCT permission.permissionName,permission.pid,permission.url
           from users LEFT JOIN users_role on users.uid = users_role.uid
                      LEFT JOIN role on users_role.rid = role.rid
                      LEFT JOIN role_permission on role.rid =role_permission.rid
                      LEFT JOIN permission on role_permission.pid = permission.pid
           where users.username = #{username}
    </select>
</mapper>

4、测试方法

package com.zj.security;
import com.zj.mapper.UsersMapper;
import com.zj.pojo.Permission;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.List;
@SpringBootTest
public class UsersMapperTest {
    @Resource
    private UsersMapper usersMapper;
    @Test
    public void testFindPermissionByUsername(){
        List<Permission> permissions = usersMapper.findPermissionByUsername("张三");
        for (Permission permission : permissions) {
            System.out.println(permission);
        }
    }
}
Permission(pid=1, permissionName=查询报表, url=/reportform/find)
Permission(pid=3, permissionName=查询税务, url=/tax/find)
Permission(pid=2, permissionName=查询工资, url=/salary/find)

5、修改MyUserDetailsService中对用户权限的处理。

package com.zj.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zj.mapper.UsersMapper;
import com.zj.pojo.Permission;
import com.zj.pojo.Users;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@Service
public class MyUserDetailsService implements UserDetailsService {
  @Resource
  private UsersMapper usersMapper;
  //自定义认正逻辑
  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    //1.构造查询条件
    QueryWrapper<Users> query = new QueryWrapper<Users>();
    query.eq("username", username);
    //2.查询用户
    Users users = new Users();
    Users users1 = users.selectOne(query);
    if (users1 == null) {
      return null;
    }
    //3.查询用户权限
    List<Permission> permissions = usersMapper.findPermissionByUsername(users1.getUsername());
    //4.将自定义权限集合转为security自带的权限集合
    List<GrantedAuthority> authorities = new ArrayList<>();
    for (Permission permission : permissions) {
      /*SimpleGrantedAuthority为GrantedAuthority接口的实现类*/
       authorities.add(new SimpleGrantedAuthority(permission.getUrl()));
    }
    //5.将查询结果封装为UserDetails对象返回
    UserDetails userDetails = User.withUsername(users1.getUsername())
            .password(users1.getPassword()).authorities(authorities).build();
    return userDetails;
  }
}

3.4 配置类设置访问控制

在给用户授权后,我们就可以给系统中的资源设置访问控制,即拥有什么权限才能访问什么资源。

1、编写控制器类,添加控制器方法资源

@RestController
public class MyController {
  @GetMapping("/reportform/find")
  public String findReportForm() {
    return "查询报表";
   }
  @GetMapping("/salary/find")
  public String findSalary() {
    return "查询工资";
   }
  @GetMapping("/staff/find")
  public String findStaff() {
    return "查询员工";
   }
}

2、修改Security配置类

//需要认证的资源
        http.authorizeHttpRequests()
                .antMatchers("/login.html").permitAll()//登录页不需要认证
                .antMatchers("/reportform/find").hasAnyAuthority("/reportform/find")
                .antMatchers("/salary/find").hasAnyAuthority("/salary/find")
                .antMatchers("/staff/find").hasAnyAuthority("/staff/find")
                .anyRequest().authenticated(); //其他请求都需要认证

3、登录并测试

搞定!!!

3.5 自定义访问控制逻辑

如果资源数量很多,一条条配置需要的权限效率较低。我们可以自定义访问控制逻辑,即访问资源时判断用户是否具有名为该资源URL的权限。

1、自定义访问逻辑

package com.zj.service;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Collection;
@Service
public class MyAuthorizationService {
    // 自定义访问控制逻辑,返回值为是否可以访问资源
    public boolean hasPermission(HttpServletRequest request, Authentication authentication){
        //1.获取认证的用户
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        //2.获取登录用户的权限
        Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
        //3.获取请求的url
        String url = request.getRequestURI();
        //4.将url封装为权限对象
        SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(url);
        //5.判断用户权限集合中是否包含请求的url权限对象
        return authorities.contains(simpleGrantedAuthority);
    }
}

2、配置类中使用自定义访问逻辑

// 权限拦截配置
        http.authorizeRequests()
                .antMatchers("/login.html").permitAll() // 表示任何权限都可以访问
                // 任何请求都使用自定义访问控制逻辑
                .anyRequest().access("@myAuthorizationService.hasPermission(request,authentication)");

3.6 @Secured设置访问控制

除了配置类,在SpringSecurity中提供了一些访问控制的注解。这些注解默认都是不可用的,需要开启后使用。

该注解是基于角色的权限控制,要求UserDetails中的权限名必须以ROLE_开头,当然数据库中的权限也是要以ROLE_开头。

1、在启动类开启注解使用

@SpringBootApplication
@MapperScan("com.zj.mapper")
@EnableGlobalMethodSecurity(securedEnabled=true)
public class SecurityApplication {
    public static void main(String[] args) {
        SpringApplication.run(SecurityApplication.class, args);
    }
}

2、在控制器方法上添加注解

@Secured("ROLE_reportform")
@GetMapping("/reportform/find")
public String findReportForm() {
  return "查询报表";
}

3.7 @PreAuthorize设置访问控制

该注解可以在方法执行前判断用户是否具有权限

1、在启动类开启注解使用

@SpringBootApplication
@MapperScan("com.zj.mapper")
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityApplication {
    public static void main(String[] args) {
        SpringApplication.run(SecurityApplication.class, args);
    }
}

2、在控制器方法上添加注解

@PreAuthorize("hasAnyAuthority('/reportform/find')")
@GetMapping("/reportform/find")
public String findReportForm() {
  return "查询报表";
}

3、取消配置类中对权限的访问配置

//       // 权限拦截配置
//        http.authorizeRequests()
//                .antMatchers("/login.html").permitAll() // 表示任何权限都可以访问
//                // 任何请求都使用自定义访问控制逻辑
//                .anyRequest().access("@myAuthorizationService.hasPermission(request,authentication)");

 

3.8 前端进行访问控制

SpringSecurity可以在一些视图技术中进行控制显示效果。例如Thymeleaf中,只有登录用户拥有某些权限才会展示一些菜单。

1、在pom中引入Spring Security和Thymeleaf的整合依赖(在创建项目选择起步依赖的时候默认就添加上了)

<!--Spring Security整合Thymeleaf-->
<dependency>
  <groupId>org.thymeleaf.extras</groupId>
  <artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

2、在Thymeleaf中使用Security标签,控制前端的显示内容

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
   xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
  <meta charset="UTF-8">
  <title>主页面</title>
</head>
<body>
<h1>主页面</h1>
<ul>
  <li sec:authorize="hasAnyAuthority('/reportform/find')"><a href="/reportform/find">查询报表</a></li>
  <li sec:authorize="hasAnyAuthority('/salary/find')"><a href="/salary/find">查询工资</a></li>
  <li sec:authorize="hasAnyAuthority('/staff/find')"><a href="/staff/find">查询员工</a></li>
</ul>
<a href="/logout">退出登录</a>
</body>
</html>

这样面对不同权限的用户,前端可以显示不同的菜单。

3.9 403处理方案

使用Spring Security时经常会看见403(无权限),这样的页面很不友好,我们可以自定义403异常处理方案:

1、编写权限不足页面noPermission.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>权限不足</title>
</head>
<body>
<h1>您的权限不足,请联系管理员!</h1>
</body>
</html>

2、编写权限不足处理类

public class MyAccessDeniedHandler implements AccessDeniedHandler {
  @Override
  public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
    response.sendRedirect("/noPermission.html");
   }
}

3、在Spring Security配置文件中配置异常处理

//异常处理
http.exceptionHandling().
        accessDeniedHandler(new MyAccessDeniedHandler());


相关实践学习
消息队列+Serverless+Tablestore:实现高弹性的电商订单系统
基于消息队列以及函数计算,快速部署一个高弹性的商品订单系统,能够应对抢购场景下的高并发情况。
云安全基础课 - 访问控制概述
课程大纲 课程目标和内容介绍视频时长 访问控制概述视频时长 身份标识和认证技术视频时长 授权机制视频时长 访问控制的常见攻击视频时长
相关文章
|
1月前
|
JSON 安全 Java
什么是JWT?如何使用Spring Boot Security实现它?
什么是JWT?如何使用Spring Boot Security实现它?
159 5
|
6月前
|
安全 Java 数据安全/隐私保护
使用Spring Security实现细粒度的权限控制
使用Spring Security实现细粒度的权限控制
|
6月前
|
安全 Java 数据库
实现基于Spring Security的权限管理系统
实现基于Spring Security的权限管理系统
|
6月前
|
安全 Java 数据安全/隐私保护
解析Spring Security中的权限控制策略
解析Spring Security中的权限控制策略
|
6月前
|
安全 Java 数据安全/隐私保护
使用Spring Security实现细粒度的权限控制
使用Spring Security实现细粒度的权限控制
|
6月前
|
安全 Java 数据安全/隐私保护
使用Java和Spring Security实现身份验证与授权
使用Java和Spring Security实现身份验证与授权
|
6月前
|
存储 安全 Java
Spring Security在企业级应用中的应用
Spring Security在企业级应用中的应用
|
3月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
254 2
|
9天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
16天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
66 14