标签:Security.登录.权限;

一、简介

SpringSecurity组件可以为服务提供安全管理的能力,比如身份验证、授权和针对常见攻击的保护,是保护基于spring应用程序的事实上的标准;

在实际开发中,最常用的是登录验证和权限体系两大功能,在登录时完成身份的验证,加载相关信息和角色权限,在访问其他系统资源时,进行权限的验证,保护系统的安全;

二、工程搭建

1、工程结构

2、依赖管理

starter-security依赖中,实际上是依赖spring-security组件的6.1.1版本,对于该框架的使用,主要是通过自定义配置类进行控制;

<!-- 安全组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${spring-boot.version}</version>
</dependency>

三、配置管理

1、核心配置类

在该类中涉及到的配置非常多,主要是服务的拦截控制,身份认证的处理流程以及过滤器等,很多自定义的处理类通过该配置进行加载;

@EnableWebSecurity
@EnableMethodSecurity
@Configuration
public class SecurityConfig { /**
* 基础配置
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
// 配置拦截规则
httpSecurity.authorizeHttpRequests(authorizeHttpRequests->{
authorizeHttpRequests
.requestMatchers(WhiteConfig.whiteList()).permitAll()
.anyRequest().authenticated();
});
// 禁用默认的登录和退出
httpSecurity.formLogin(AbstractHttpConfigurer::disable);
httpSecurity.logout(AbstractHttpConfigurer::disable);
httpSecurity.csrf(AbstractHttpConfigurer::disable); // 异常时认证处理流程
httpSecurity.exceptionHandling(exeConfig -> {
exeConfig.authenticationEntryPoint(authenticationEntryPoint());
}); // 添加过滤器
httpSecurity.addFilterAt(authTokenFilter(),CsrfFilter.class);
return httpSecurity.build() ;
} @Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
} @Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return new AuthExeHandler();
} @Bean
public OncePerRequestFilter authTokenFilter () {
return new AuthTokenFilter();
} /**
* 认证管理
*/
@Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(authenticationProvider()) ;
} /**
* 自定义用户认证流
*/
@Bean
public AbstractUserDetailsAuthenticationProvider authenticationProvider() {
return new AuthProvider() ;
}
}

2、认证数据源

UserDetailsService是加载用户特定数据的核心接口,编写用户服务类并实现该接口,提供用户信息和权限体系的数据查询和加载,作为用户身份识别的关键凭据;

@Service
public class UserService implements UserDetailsService { @Resource
private UserBaseMapper userBaseMapper;
@Resource
private BCryptPasswordEncoder passwordEncoder; @Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
UserBase queryUser = geyByUserName(userName);
if (Objects.isNull(queryUser)){
throw new AuthException("该用户不存在");
}
List<GrantedAuthority> grantedAuthorityList = new ArrayList<>() ;
grantedAuthorityList.add(new SimpleGrantedAuthority(queryUser.getUserRole())) ;
return new User(queryUser.getUserName(),queryUser.getPassWord(),grantedAuthorityList);
} public int register (UserBase userBase){
if (!Objects.isNull(userBase)){
userBase.setPassWord(passwordEncoder.encode(userBase.getPassWord()));
userBase.setCreateTime(new Date()) ;
return userBaseMapper.insert(userBase) ;
}
return 0 ;
} public UserBase getById (Integer id){
return userBaseMapper.selectById(id) ;
} public UserBase geyByUserName (String userName){
List<UserBase> userBaseList = new LambdaQueryChainWrapper<>(userBaseMapper)
.eq(UserBase::getUserName,userName).last("limit 1").list();
if (userBaseList.size() > 0){
return userBaseList.get(0) ;
}
return null ;
}
}

3、认证流程

自定义用户名和密码的身份令牌认证逻辑,基于用户名Username从上面的用户服务类中加载数据并校验,在验证成功后将用户的身份令牌返回给调用者;

@Component
public class AuthProvider extends AbstractUserDetailsAuthenticationProvider {
private static final Logger log = LoggerFactory.getLogger(AuthProvider.class); @Resource
private UserService userService;
@Resource
private BCryptPasswordEncoder passwordEncoder; @Override
protected void additionalAuthenticationChecks(
UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
User user = (User) userDetails;
String loginPassword = authentication.getCredentials().toString();
log.info("user:{},loginPassword:{}",user.getPassword(),loginPassword);
if (!passwordEncoder.matches(loginPassword, user.getPassword())) {
throw new AuthException("账号或密码错误");
}
authentication.setDetails(user);
}
@Override
protected UserDetails retrieveUser(
String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
log.info("username:{}",username);
return userService.loadUserByUsername(username);
}
}

4、身份过滤器

通过继承OncePerRequestFilter抽象类,实现用户身份的过滤器,如果不是白名单请求,需要验证令牌是否正确有效,SecurityContextHolder默认状态下使用ThreadLocal存储信息;

@Component
public class AuthTokenFilter extends OncePerRequestFilter {
@Resource
private AuthTokenService authTokenService ;
@Resource
private AuthExeHandler authExeHandler ; @Override
protected void doFilterInternal(@Nonnull HttpServletRequest request,
@Nonnull HttpServletResponse response,
@Nonnull FilterChain filterChain) throws ServletException, IOException {
String uri = request.getRequestURI();
if (Arrays.asList(WhiteConfig.whiteList()).contains(uri)){
// 如果是白名单直接放行
filterChain.doFilter(request,response);
} else {
String token = request.getHeader("Auth-Token");
if (Objects.isNull(token) || token.isEmpty()){
// Token不存在,拦截返回
authExeHandler.commence(request,response,null);
} else {
Object object = authTokenService.getToken(token);
if (!Objects.isNull(object) && object instanceof User user){
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(user, null,user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request,response);
} else {
// Token验证失败,拦截返回
authExeHandler.commence(request,response,null);
}
}
}
}
}

四、核心功能

1、登录退出

自定义登录退出两个接口,基于用户名和密码执行上述的身份认证流程,如果认证成功则返回用户的身份令牌,在请求「非」白名单接口时需要在请求头中Auth-Token:token携带该令牌,在退出时会清除身份信息;

@Service
public class LoginService { private static final Logger log = LoggerFactory.getLogger(LoginService.class); @Resource
private AuthTokenService authTokenService ;
@Resource
private AuthenticationManager authenticationManager; public String doLogin (UserBase userBase){
AbstractAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userBase.getUserName().trim(), userBase.getPassWord().trim());
Authentication authentication = authenticationManager.authenticate(authToken) ;
User user = (User) authentication.getDetails();
return authTokenService.createToken(user) ;
} public Boolean doLogout (String authToken){
SecurityContextHolder.clearContext();
return authTokenService.deleteToken(authToken) ;
}
} @Service
public class AuthTokenService { private static final Logger log = LoggerFactory.getLogger(AuthTokenService.class);
@Resource
private RedisTemplate<String,Object> redisTemplate ; public String createToken (User user){
String userName = user.getUsername();
String token = DigestUtils.md5DigestAsHex(userName.getBytes());
log.info("user-name:{},create-token:{}",userName,token);
redisTemplate.opsForValue().set(token,user,10, TimeUnit.MINUTES);
return token ;
} public Object getToken (String token){
return redisTemplate.opsForValue().get(token);
} public Boolean deleteToken (String token){
return redisTemplate.delete(token);
}
}

2、权限校验

UserWeb类中提供用户的注册接口,在用户表中创建两个测试用户:admin对应ROLE_Admin角色,user对应ROLE_User角色,验证如下几个接口的权限控制;

select接口不需要鉴权,拦截器放行即可访问;getUser接口校验ROLE_User角色;getAdmin接口校验ROLE_Admin角色;query接口校验两个角色中的任意一个即可;

两个不同用户登录获取到各自的身份令牌,使用不同的令牌请求接口,在PreAuthorize验证通过后才可以正常访问;

@RestController
public class UserWeb { @Resource
private UserService userService ; @PostMapping("/register")
public String register (@RequestBody UserBase userBase){
return "register-"+userService.register(userBase) ;
} @GetMapping("/select/{id}")
public UserBase select (@PathVariable Integer id){
return userService.getById(id) ;
} @PreAuthorize("hasRole('User')")
@GetMapping("/user/{id}")
public UserBase getUser (@PathVariable Integer id){
return userService.getById(id) ;
} @PreAuthorize("hasRole('Admin')")
@GetMapping("/admin/{id}")
public UserBase getAdmin (@PathVariable Integer id){
return userService.getById(id) ;
} @PreAuthorize("hasAnyRole('User','Admin')")
@GetMapping("/query/{id}")
public UserBase query (@PathVariable Integer id){
return userService.getById(id) ;
}
}

五、参考源码

文档仓库:
https://gitee.com/cicadasmile/butte-java-note 源码仓库:
https://gitee.com/cicadasmile/butte-spring-parent

SpringBoot3安全管理的更多相关文章

  1. BPM生产安全管理解决方案分享

    一.方案概述生产安全管理是企业生产管理的重要组成部分,组织实施好企业安全管理规划.指导.检查和决策,保证生产处于最佳安全状态是安全管理的重要内容和职责.H3 BPM企业生产安全管理解决方案是一套专门为 ...

  2. java安全管理器SecurityManager入门

    table { margin-left: 30px; width: 95%; border: 1px; border-collapse: collapse } img { border: 1px so ...

  3. java安全沙箱(四)之安全管理器及Java API

    java是一种类型安全的语言,它有四类称为安全沙箱机制的安全机制来保证语言的安全性,这四类安全沙箱分别是: 类加载体系 .class文件检验器 内置于Java虚拟机(及语言)的安全特性 安全管理器及J ...

  4. SQL Server 数据库的安全管理(登录、角色、权限)

    ---数据库的安全管理 --登录:SQL Server数据库服务器登录的身份验证模式:1)Windows身份验证.2)Windows和SQL Server混合验证 --角色:分类:1)服务器角色.服务 ...

  5. Tomcat 6 --- 你很少使用的安全管理SecurityManager

    试想一下,如果你的JSP页面中包含一句代码“System.exit(1);”,你的web应用访问到该JSP时,会发生什么? 一般使用tomcat可能都没有注意到这个问题,本篇主要讲述tomcat 6中 ...

  6. ch4 MySQL 安全管理

    第 4 章 MySQL 安全管理 前言 对于任何一个企业来说,其数据库系统中所保存数据的安全性无疑是非常重要的,尤其是公司的有些商业数据,可能数据就是公司的根本,失去了数据的安全性,可能就是失去了公司 ...

  7. 企业级 Linux 安全管理实例(1)

    公司企业多用Linux服务器,其中涉及到的一些安全管理对于安全运维人员来说是必不可少的应知技能, 以下案例沿着背景->需求->具体要求->操作步骤的流程进行描述,可以加深对安全管理的 ...

  8. [企业级linux安全管理]- 安全管理基础(1)

    1. 操作条件:  (1)装有 Cent OS Linux 操作系统的虚拟机一台 2. 背景: 某企业有一台服务器,其信息如下: (1)  该服务器上存在管理员 root,密码为 root,另存有一些 ...

  9. java jvm学习笔记六(实现写自己的安全管理器)

    安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用AccessController的checkPerssiom方法,访问控 ...

  10. java jvm学习笔记四(安全管理器)

    欢迎装载请说明出处:http://blog.csdn.net/yfqnihao 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一 ...

随机推荐

  1. 项目打包后配置到node服务器

    1.将项目进行打包 npm run build项目根目录下会多出一个打包好的由.js .html .css文件组成的dist文件夹,如图 2.搭建node微型服务器   新建文件夹命名"no ...

  2. pg数据库的备份和恢复以及sql脚本错误的解决方法

    1.备份单库单表的数据,以insert语句的方式 pg_dump -h IP -p 端口 -U 用户名 -t 表名 --inserts –f dbname.sql 数据库名 pg_dump -h 17 ...

  3. 2022-08-21:以下go语言代码输出什么?A:0;B:panic;C:不知道。 package main var n = -99 func main() { m := make(map[

    2022-08-21:以下go语言代码输出什么?A:0:B:panic:C:不知道. package main var n = -99 func main() { m := make(map[stri ...

  4. 2021-02-06:假设字符串str长度为N,请问最长回文子串的长度是多少?

    福哥答案2021-02-06: 1.动态规划.无代码,见图.2.中心扩展法.无代码.3.Manacher算法.有代码,见图.1)理解回文半径数组.2)理解所有中心的回文最右边界R,和取得R时的中心点C ...

  5. 2021-05-09:给定数组hard和money,长度都为N;hard[i]表示i号的难度, money[i]表示i号工作的收入;给定数组ability,长度都为M,ability[j]表示j号人的

    2021-05-09:给定数组hard和money,长度都为N:hard[i]表示i号的难度, money[i]表示i号工作的收入:给定数组ability,长度都为M,ability[j]表示j号人的 ...

  6. Matplotlib.pyplot.plot 绘图

    Matplotlib.pyplot 创建图形.在图形中创建创建一个绘图区域.在绘图区域中你那个绘制一些线.在图形中添加标签之类 画二维平面图 x = np.arange(0, 10, 2) y1 = ...

  7. OpenAI 官宣首个 ChatGPT iOS 应用

    最近,OpenAI 宣布推出官方 iOS 应用,允许用户随时随地访问其高人气 AI 聊天机器人,此举也打破了近几个月内苹果 App Store 上充斥似是而非的山寨服务的窘境. 该应用程序是 Chat ...

  8. 解密Prompt7. 偏好对齐RLHF-OpenAI·DeepMind·Anthropic对比分析

    前三章都围绕指令微调,这一章来唠唠RLHF.何为优秀的人工智能?抽象说是可以帮助人类解决问题的AI, 也可以简化成3H原则:Helpful + Honesty + Harmless.面向以上1个或多个 ...

  9. [MAUI]模仿Chrome下拉标签页的交互实现

    @ 目录 创建粘滞效果的圆控件 贝塞尔曲线绘制圆 创建控件 创建形变 可控形变 形变边界 形变动画 创建手势控件 创建页面布局 更新拖拽物位置 其它细节 项目地址 今天来说说怎样在.NET MAUI中 ...

  10. 如何不使用图形来创建ACFS文件系统

    客户需求,提供在19c环境下,ACFS的命令行操作的具体步骤,便于在图形界面不可用场景使用. 当然,如果有图形可操作,还是推荐首选图形,避免复杂度以及不必要的错误. 其实之前有测试过11g环境下的AC ...