1、功能实现

1.开启基于方法的安全认证机制
2.自定义实现细粒度的权限控制

2、security03 子工程

学习本篇前最好对 入门篇 有所了解

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.yzm</groupId><artifactId>security</artifactId><version>0.0.1-SNAPSHOT</version><relativePath>../pom.xml</relativePath> <!-- lookup parent from repository --></parent><artifactId>security03</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>security03</name><description>Demo project for Spring Boot</description><dependencies><dependency><groupId>com.yzm</groupId><artifactId>common</artifactId><version>0.0.1-SNAPSHOT</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

application.yml

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.192.128:3306/testdb?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghaiusername: rootpassword: 1234# 启动时提示需要这个,加上就好了main:allow-bean-definition-overriding: truemybatis-plus:mapper-locations: classpath:/mapper/*Mapper.xmltype-aliases-package: com.yzm.security03.entityconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3、自定义UserDetailsService 实现类

package com.yzm.security03.config;import com.yzm.security03.entity.Role;
import com.yzm.security03.entity.User;
import com.yzm.security03.service.RoleService;
import com.yzm.security03.service.UserService;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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 java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;/*** 自定义UserDetailsService,重写loadUserByUsername方法,查询数据库获取用户信息和权限信息* Spring Security进行用户认证时,需要根据用户的账号、密码、权限等信息进行认证,* 因此,需要根据查询到的用户信息封装成一个认证用户对象并交给Spring Security进行认证。* 查询用户信息并封装成认证用户对象的过程是在UserDetailsService接口的实现类(需要用户自己实现)中完成的*/
@Service
public class SecUserDetailsServiceImpl implements UserDetailsService {private final UserService userService;private final RoleService roleService;public SecUserDetailsServiceImpl(UserService userService, RoleService roleService) {this.userService = userService;this.roleService = roleService;}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userService.lambdaQuery().eq(User::getUsername, username).one();if (user == null) {throw new UsernameNotFoundException(String.format("用户'%s'不存在", username));}List<Integer> roleIds = Arrays.stream(user.getRIds().split(",")).map(Integer::parseInt).collect(Collectors.toList());List<Role> roleList = roleService.listByIds(roleIds);List<SimpleGrantedAuthority> authorities = roleList.stream().map(Role::getRName).distinct().map(SimpleGrantedAuthority::new).collect(Collectors.toList());return new SecUserDetails(username, user.getPassword(), authorities, roleIds);}
}

由于我需要一些自定义的属性信息,默认org.springframework.security.core.userdetails.User满足不了需求,所以自定义了SecUserDetails 继承User,为后面使用细粒度权限作准备

package com.yzm.security03.config;import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;import java.util.Collection;
import java.util.List;
import java.util.Set;/*** 自定义 安全用户模型* loadUserByUsername 的返回值是UserDetails,这是一个接口,* 可以使用它的子类org.springframework.security.core.userdetails.User* 也可以自定义一个,新增一些自定义的东西*/
public class SecUserDetails extends User {private static final long serialVersionUID = 3033317408164827323L;// 自定义属性private List<Integer> roleIds;private Set<String> permissions;public SecUserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities) {this(username, password, true, true, true, true, authorities);}public SecUserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities, List<Integer> roleIds) {this(username, password, true, true, true, true, authorities);this.roleIds = roleIds;}public SecUserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities, Set<String> permissions) {this(username, password, true, true, true, true, authorities);this.permissions = permissions;}public SecUserDetails(String username,String password,boolean enabled,boolean accountNonExpired,boolean credentialsNonExpired,boolean accountNonLocked,Collection<? extends GrantedAuthority> authorities) {super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);}public List<Integer> getRoleIds() {return roleIds;}public void setRoleIds(List<Integer> roleIds) {this.roleIds = roleIds;}public Set<String> getPermissions() {return permissions;}public void setPermissions(Set<String> permissions) {this.permissions = permissions;}
}

4、SecurityConfig 配置类

开启注解 @EnableGlobalMethodSecurity(prePostEnabled = true)

package com.yzm.security03.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;@Slf4j
@Configuration
@EnableWebSecurity // 开启Security服务
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启全局注解
public class SecurityConfig extends WebSecurityConfigurerAdapter {private final UserDetailsService userDetailsService;private final SecPermissionEvaluator permissionEvaluator;public SecurityConfig(@Qualifier("secUserDetailsServiceImpl") UserDetailsService userDetailsService, SecPermissionEvaluator permissionEvaluator) {this.userDetailsService = userDetailsService;this.permissionEvaluator = permissionEvaluator;}/*** 密码编码器* passwordEncoder.encode是用来加密的,passwordEncoder.matches是用来解密的*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}/*** 配置用户* 指定默认从哪里获取认证用户的信息,即指定一个UserDetailsService接口的实现类*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {// 从数据库读取用户、并使用密码编码器解密auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());}//配置资源权限规则@Overrideprotected void configure(HttpSecurity http) throws Exception {http// 关闭CSRF跨域.csrf().disable()// 登录.formLogin().loginPage("/auth/login") //指定登录页的路径,默认/login.loginProcessingUrl("/login") //指定自定义form表单请求的路径(必须跟login.html中的form action=“url”一致).defaultSuccessUrl("/home", true) // 登录成功后的跳转url地址.failureUrl("/auth/login?error") // 登录失败后的跳转url地址.permitAll().and().exceptionHandling().accessDeniedPage("/401") // 拒接访问跳转页面.and()// 退出登录.logout().permitAll().and()// 访问路径URL的授权策略,如注册、登录免登录认证等.authorizeRequests().antMatchers("/", "/home", "/register", "/auth/login").permitAll() //指定url放行.anyRequest().authenticated() //其他任何请求都需要身份认证.and();}
}

5、注解使用

@PreAuthorize(“hasAuthority(‘ROLE_ADMIN’)”)
@PreAuthorize(“hasAnyRole(‘ADMIN’,‘USER’)”)

package com.yzm.security03.controller;import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/admin")
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
public class AdminController {@GetMappingpublic Object admin(@AuthenticationPrincipal UserDetails userDetails) {return userDetails;}@GetMapping("/select")public Object select() {return "Select";}@GetMapping("/create")public Object create() {return "Create";}@GetMapping("/update")public Object update() {return "Update";}@GetMapping("/delete")public Object delete() {return "Delete";}
}
package com.yzm.security03.controller;import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
@PreAuthorize("hasAnyRole('ADMIN','USER')")
public class UserController {@GetMappingpublic Object user(@AuthenticationPrincipal UserDetails userDetails) {return userDetails;}...@GetMapping("/delete")public Object delete() {return "Delete";}
}

6、html 页面

参考上一篇 入门篇

7、测试注解

启动项目,登录yzm,访问 /user/** 接口

admin可以访问所有接口

8、细粒度权限控制@PreAuthorize(“hasPermission()”)

目前演示都是基于角色进行授权的,如果要角色role跟权限permission一起使用,就需要我们自定义了
SecPermissionEvaluator 实现 PermissionEvaluator 重写 hasPermission()

package com.yzm.security03.config;import com.yzm.security03.entity.Permissions;
import com.yzm.security03.entity.Role;
import com.yzm.security03.service.PermissionsService;
import com.yzm.security03.service.RoleService;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;import java.io.Serializable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;/*** 细粒度的权限控制*/
@Slf4j
@Component
public class SecPermissionEvaluator implements PermissionEvaluator {private final RoleService roleService;private final PermissionsService permissionsService;public SecPermissionEvaluator(RoleService roleService, PermissionsService permissionsService) {this.roleService = roleService;this.permissionsService = permissionsService;}@Overridepublic boolean hasPermission(Authentication authentication, Object targetUrl, Object targetPermission) {log.info("hasPermission 方法执行");// 获得loadUserByUsername()方法的结果SecUserDetails userDetails = (SecUserDetails) authentication.getPrincipal();// 获得loadUserByUsername()中注入的角色//Collection<GrantedAuthority> authorities = userDetails.getAuthorities();// 拿到SecUserDetails 自定义的roleIds值List<Integer> roleIds = userDetails.getRoleIds();// 查询角色对应的权限id信息List<Role> roles = roleService.listByIds(roleIds);Set<Integer> permIds = new HashSet<>();roles.forEach(role -> {Set<Integer> collect = Arrays.stream(role.getPIds().split(",")).map(Integer::parseInt).collect(Collectors.toSet());permIds.addAll(collect);});List<Permissions> perms = permissionsService.listByIds(permIds);String permission = (String) targetUrl + ":" + (String) targetPermission;for (Permissions perm : perms) {// 如果访问的Url和权限用户符合的话,返回trueif (permission.equals(perm.getPName())) {return true;}}return false;}@Overridepublic boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {return false;}}

SecurityConfig中把自定义SecPermissionEvaluator交给Security管理

    /*** 注入自定义PermissionEvaluator*/@Beanpublic DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() {DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();handler.setPermissionEvaluator(permissionEvaluator);return handler;}

使用@PreAuthorize(“hasPermission(’’,’’)”)

package com.yzm.security03.controller;import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/admin")
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
public class AdminController {@GetMapping("/select")@PreAuthorize("hasPermission('admin','select')")public Object select() {return "Select";}@GetMapping("/create")public Object create() {return "Create";}@GetMapping("/update")public Object update() {return "Update";}@GetMapping("/delete")@PreAuthorize("hasPermission('admin','delete')")public Object delete() {return "Delete";}
}
package com.yzm.security03.controller;import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
@PreAuthorize("hasAnyRole('ADMIN','USER')")
public class UserController {@GetMapping("/select")@PreAuthorize("hasPermission('user','select')")public Object select() {return "Select";}@GetMapping("/create")public Object create() {return "Create";}@GetMapping("/update")public Object update() {return "Update";}@GetMapping("/delete")@PreAuthorize("hasPermission('user','delete')")public Object delete() {return "Delete";}
}

9、测试细粒度权限

登录yzm,/user/select、/user/delete时会打印日志,并且由于/user/delete没有权限,拒绝访问

相关链接

首页
上一篇:入门篇
下一篇:记住我篇

Security之注解篇相关推荐

  1. 循序渐进学spring security 第八篇,如何配置密码加密?是否支持多种加密方案?

    文章目录 回顾 密码明文会带来什么问题? 如何加密? PasswordEncoder 加密接口 如何配置? 加密的密码在登录的时候是怎么校验的? 默认的加密是什么? DaoAuthentication ...

  2. Spring DI(依赖注入)注解篇

    1 课程内容介绍 我之前写的的一篇博客Spring核心功能DI(依赖注入)xml篇主要介绍了如何通过配置xml的方式来实现依赖注入,今天我们来介绍如何通过注解方式完成我们的依赖注入操作. 2 注入基本 ...

  3. Security之基础篇

    1.功能实现 创建内存用户,进行登录 访问权限接口 2.security01 子工程 <?xml version="1.0" encoding="UTF-8&quo ...

  4. 开发框架-Spring(2) - 注解篇

    1.Bean基础配置 Spring除了上一篇提到的xml配置文件进行配置之外,还可以使用注解方式进行配置,注解方式慢慢称为xml配置的替代方案.我们有了xml开发的经验,学习注解开发就方便了许多,注解 ...

  5. java进阶之注解篇

    文章目录 注解 基本语法 定义注解 元注解 编写注解处理器 注解元素 默认值限制 生成外部文件 替代方案 注解不支持继承 实现处理器 注解 注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化 ...

  6. Java注解篇(一)四大元注解@Target@Retention@Documented注解

    1.@Target目标注解,意思带注解的适用于哪些范围,有以下参数可供使用 @Target(ElementType.METHOD) 1.CONSTRUCTOR:用于描述构造器 2.FIELD:用于描述 ...

  7. IdaP02: springboot security权限控制篇

    通过狂神发布在B站的视频,了解到spring security(以下简称ss)在权限控制的强大好用,故学之. 一.详细例子 数据库 pom.xml 导入必要的包 <!-- security和th ...

  8. 每天进步一点点~注解篇

    收藏从未停止,学习从未开始, 与其长篇大论,不如简短精悍. 每天进步一点点,目标距离缩小点. 每天进步一点点,成功就会在眼前. 本人水平和能力有限,文章如有不对或有需要修改的地方,还望各位指明纠正 1 ...

  9. 关于@Primary和@Autowired+@Qualified和@Resource注解的区别【注解篇】

     ​  ​ ​  ​ ​  ​ ​  ​ ​  ​ ​  ​ ​  ​ ​  ​ ​  ​ ​  ​ ​  ​ ​  ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ 

最新文章

  1. linux containerd 容器 简介
  2. [Django 3.2.8] SimpleUI的分页问题
  3. 如何在MyEclipse中添加 用户自定义类库 以及将自定义的类库加入工程的加载目录...
  4. 网络4/7层模型各层作用和协议对比
  5. MailMail升级到1.0.2.4
  6. 噪音 - Perlin Noise
  7. ARM CORTEX-M0 权威指南 英文
  8. eclipse占用内存过大_Java内存泄漏分析工具Memory Analyzer Tool
  9. 如何搞定高并发系统设计?
  10. python如何查询文件路径_Python使用os.listdir和os.walk获取文件路径
  11. Python基础知识笔记(一)
  12. 2018年视频云服务市场格局进入整合阶段,阿里云视频云位居市场竞争力领导者的位置... 1
  13. discuz开启url伪静态
  14. Android 增强版百分比布局库 为了适配而扩展
  15. 计算机网络规范化发生在,关于计算机网络系统工程设计工作规范化的几点建议.pdf...
  16. 《PMP学习笔记》11.7 子过程:实施风险应对
  17. 一个空间多个php网站,一个空间多个域名多个网站的办法汇总
  18. fftn N 维快速傅里叶变换(Matlab)
  19. 【最新面试】2022年软件测试面试题大全(持续更新)附答案
  20. Java程序员转行可以做什么?程序员

热门文章

  1. 利用service实现音乐的后台播放
  2. 重玩 40 年前的经典游戏小蜜蜂,这次通关了源码
  3. Python和JavaScript的主要区别
  4. 【matlab】绘制云图 + 修改配色方案 + 保存配色方案
  5. Java知识点总结(超详细)
  6. Java程序员常读书单,附下载地址
  7. 【2022南京大学操作系统(蒋岩炎)】(一)操作系统概述 | 操作系统上的程序
  8. Linux批量加连续端口映射,批量端口映射
  9. 苹果CMSv10自适应好看的短视频模板直接播放提升用户体验
  10. NVMe驱动注释(持续更新)