标签: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. 2023-04-05:做甜点需要购买配料,目前共有n种基料和m种配料可供选购。 制作甜点需要遵循以下几条规则: 必须选择1种基料;可以添加0种、1种或多种配料,每种类型的配料最多添加2份, 给定长度为

    2023-04-05:做甜点需要购买配料,目前共有n种基料和m种配料可供选购. 制作甜点需要遵循以下几条规则: 必须选择1种基料:可以添加0种.1种或多种配料,每种类型的配料最多添加2份, 给定长度为 ...

  2. 2022-06-11:注意本文件中,graph不是邻接矩阵的含义,而是一个二部图。 在长度为N的邻接矩阵matrix中,所有的点有N个,matrix[i][j]表示点i到点j的距离或者权重, 而在二部

    2022-06-11:注意本文件中,graph不是邻接矩阵的含义,而是一个二部图. 在长度为N的邻接矩阵matrix中,所有的点有N个,matrix[i][j]表示点i到点j的距离或者权重, 而在二部 ...

  3. 配置pip源

    1.使用配置文件配置文件[global]trusted-host=pypi.doubanio.comindex-url=https://pypi.doubanio.com/simple配置文件放置位置 ...

  4. 一篇文章告诉你什么是Java内存模型

    在上篇 并发编程Bug起源:可见性.有序性和原子性问题,介绍了操作系统为了提示运行速度,做了各种优化,同时也带来数据的并发问题, 定义 在单线程系统中,代码按照顺序从上往下顺序执行,执行不会出现问题. ...

  5. ENVI指定像元数量(行数与列数)裁剪栅格图像

      本文介绍基于ENVI软件,实现栅格遥感影像按照像元行列号与个数进行指定矩形区域裁剪的方法.   一般的,如果我们需要裁剪某个具体的行政区域,按照对应区域的矢量图层裁剪即可:如果需要裁剪某个大致的区 ...

  6. find提权

    更新中.............. find 常用参数 语法:find [path-] [expression] path为查找路径,.为当前路径,/为根目录 expression即为参数 -name ...

  7. 汇总低效的SQL语句

    背景 SQL专家云像"摄像头"一样,对环境.参数配置.服务器性能指标.活动会话.慢语句.磁盘空间.数据库文件.索引.作业.日志等几十个运行指标进行不同频率的实时采集,保存到SQL专 ...

  8. [AGC055B] ABC Supremacy 题解

    [AGC055B] ABC Supremacy 题解 题目描述 给定两个长度为 \(n\) 的字符串 \(a\),\(b\). 你可以进行若干次以下操作: 若 \(a\) 中的一个子串为 ABC,BC ...

  9. CMU15445 (Fall 2020) 数据库系统 Project#2 - B+ Tree 详解(下篇)

    前言 上一篇博客中实现了单线程 B+ 树的查找.插入.删除和迭代操作,这篇博客将完成实验二的剩余任务:并发 B+ 树.实现 B+ 树并发访问最简单的方法就是在整棵树上加一把大锁,但是这样会导致过多线程 ...

  10. 自然语言处理 Paddle NLP - 快递单信息抽取 (ERNIE 1.0)

    文档检索:需要把业务问题拆解成子任务.文本分类 -> 文本匹配 -> 等任务 -> Panddle API 完成子任务 -> 子任务再拼起来 介绍 在2017年之前,工业界和学 ...