【Spring Security框架解析】

文章目录

  • Spring-security介绍
  • Spring-security认证授权流程
    • 认证流程
    • Security流程
    • 认证过滤器实现
    • 获取UserDetail信息
  • 配置Security

Spring-security介绍

Spring Security是一个功能强大且高度可定制的Java安全框架,用于保护基于Spring的应用程序。它提供了全面的安全服务,包括认证(Authentication)、授权(Authorization)、防止CSRF等。

  1. 认证(Authentication)
    认证是确认用户身份的过程。Spring Security支持多种认证机制,如表单登录、HTTP基本认证、OAuth2、LDAP等。
  2. 授权(Authorization)
    授权是确定用户是否有权限访问特定资源的过程。Spring Security提供了基于角色的访问控制(RBAC)和基于属性的访问控制(ABAC)。
  3. 防止CSRF攻击
    Spring Security自动保护你的应用程序免受跨站请求伪造(CSRF)攻击。
  4. 会话管理
    提供会话固定攻击的保护,并支持会话超时和并发会话控制。
  5. 输入验证
    防止常见的安全漏洞,如SQL注入和跨站脚本(XSS)。
  6. 方法级安全性
    使用Spring AOP,你可以在方法级别上实现安全性控制,例如,只有具有特定角色的用户才能访问特定的方法。
  7. 异常处理
    提供了一个异常处理机制,允许你自定义安全相关的异常处理。
  8. 集成Spring Boot
    Spring Security与Spring Boot集成良好,提供了自动配置和简化的配置选项。
  9. 支持多种认证提供者
    可以与各种认证提供者(如数据库、LDAP、OAuth2提供者等)集成。
  10. 自定义认证和授权
    Spring Security允许你自定义认证和授权逻辑,以满足特定需求。
  11. 单点登录(SSO)
    支持单点登录解决方案,允许用户使用一组凭据访问多个应用程序。
  12. 安全通信
    支持HTTPS和其他安全通信协议,以保护数据传输。

Spring-security认证授权流程

认证流程

在这里插入图片描述
Authentication接口: 它的实现类,表示当前访问系统的用户,封装了用户相关信息。
AuthenticationManager接口:定义了认证authenticate()的方法
UserDetailsService接口:加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的方法。
UserDetails接口:提供核心用户信息。通过UserDetailsService根据用户名获取处理的用户信息要封装成UserDetails对象返回。然后将这些信息封装到Authentication对象中。

Security流程

实现步骤:
1.构建一个自定义的service接口,实现SpringSecurity的UserDetailService接口。
2.建一个service实现类,实现此loadUserByUsername方法。
3.调用登录的login接口,会经过authenticationManager.authenticate(authenticationToken)方法。此方法会调用loadUserByUsername方法。
4.方法内部做用户信息的查询,判断用户名和密码是否正确,这是第一道认证。
5.如果没有查到信息就抛出异常。
6.如果查到信息了再接着查用户的权限信息,返回权限信息到SysUser实体。
7.此实体实现了SpringSecurity自带的userDetail接口。实现了getAuthorities方法。
8.每次查询权限都会调用此方法。
9.查询到的权限,会被返回到login接口。进行后续操作。
10.如果认证通过,通过身份信息中的userid生产一个jwt。
11.把完整的用户信息作为value,token作为key存入redis。

认证过滤器实现

代码入口 通过继承UsernamePasswordAuthenticationFilter过滤器来实现认证。

@Slf4j
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {private final AuthenticationManager authenticationManager;public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {this.authenticationManager = authenticationManager;super.setFilterProcessesUrl("/user/login");}/*** 设置登录方式的认证实体*/@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {log.info("<==============登录认证开始===============>");SysUser user;Authentication authenticate;try {//获取用户传送的信息user = new ObjectMapper().readValue(request.getInputStream(), SysUser.class);//参数校验if (user == null) {throw new BusinessException("登录信息不能为空");}if(!StringUtils.hasText(user.getLoginTabs())){throw new BusinessException("登录类型不能为空");}LoginEnum value = LoginEnum.getValue(user.getLoginTabs());if(value == null){throw new BusinessException("登录类型未匹配");}//登录认证不需要抽离 只依赖Security 鉴权 目前只有手机号和账密登录后续自行叠加switch (value){case SYS_LOG_TYPE_1:authenticate = authenticationManager.authenticate(new UserDetailAuthenticationToken(user));break;case SYS_LOG_TYPE_2:authenticate = authenticationManager.authenticate(new MobileCodeAuthenticationToken(user.getPhone(), user.getPhoneCode()));break;default:throw new BusinessException("登录类型错误");}return authenticate;} catch (Exception e) {log.error("登录认证失败:",e);throw new BadCredentialsException(e.getMessage());}}/**认证成功生成token并返回*/@Overrideprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException {log.info("<==============登录认证成功===============>");JWTResponseUtils.successfulAuthentication(request, response,chain, authResult);}/**认证失败,返回失败原因*/@Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {log.info("<==============登录认证失败:{}===============>", failed.getMessage());JWTResponseUtils.unsuccessfulAuthentication(request, response, failed);}

获取UserDetail信息

*** 账号认证器处理器只监听UserDetailAuthenticationToken*/
@Slf4j
@SuppressWarnings("all")
public class UserDetailAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {private final UserDetailsService userDetailsService;private final RedisKeyUtil redisKeyUtil;private final PasswordEncoder passwordEncoder;public UserDetailAuthenticationProvider(UserDetailsService userDetailsService, RedisKeyUtil redisKeyUtil, PasswordEncoder passwordEncoder){this.userDetailsService = userDetailsService;this.redisKeyUtil = redisKeyUtil;this.passwordEncoder = passwordEncoder;}@Overrideprotected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {}@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {UserDetailAuthenticationToken tokenReq = (UserDetailAuthenticationToken) authentication;String loginElement = Constants.STATE_INVALID;try{//获取是否开启登录要素校验loginElement = redisKeyUtil.get(RedisEnums.REDIS_LOGIN_ELEMENT.getCode());log.info("查看登录要素入参: key = "+RedisEnums.REDIS_LOGIN_ELEMENT.getCode()+"; value = "+loginElement);}catch (Exception exception){log.error("查看登录要素出现异常,入参: key = "+RedisEnums.REDIS_LOGIN_ELEMENT.getCode()+"; exception: ",exception);}log.info("<==============账号认证处理器处理开始===============>");try {// 根据账号,获取登录人员信息UserDetails userDetails = userDetailsService.loadUserByUsername(tokenReq.getUsername());//储存用户信息必须是SecurityUtils类SecurityUtils securityUtils = (SecurityUtils)userDetails;if(Constants.STATE_EFFECTIVE.equals(loginElement)){if(!StringUtils.hasText(tokenReq.getPhone())){throw new BusinessException("手机号不能为空");}if(!StringUtils.hasText(tokenReq.getPhoneCode())){throw new BusinessException("验证码不能为空");}String redisPhoneCode = redisKeyUtil.get(RedisEnums.REDIS_LOGIN_KEY.getCode() + tokenReq.getPhone());if(!StringUtils.hasText(redisPhoneCode)){throw new BusinessException("验证码已过期,请重新获取验证码");}if(!tokenReq.getPhoneCode().equals(redisPhoneCode)){throw new BusinessException("验证码不正确,请重新填写");}if(!tokenReq.getPhone().equals(securityUtils.getPhone())){throw new BusinessException("用户手机号绑定不正确,请重新填写");}}boolean matches = passwordEncoder.matches(tokenReq.getPassword(), securityUtils.getPassword());if(!matches){throw new BusinessException("密码不正确,请重新填写");}UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());log.info("<==============账号认证处理器处理结束===============>");return usernamePasswordAuthenticationToken;} catch (BusinessException e) {log.error("账号登录出现可控异常:"+e.getMessage());throw new BadCredentialsException(e.getMessage());} catch (Exception e) {log.error("账号登录出现不可控异常:",e);throw new BadCredentialsException("账号登录认证异常");}}@Overrideprotected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {return null;}@Overridepublic boolean supports(Class<?> authentication) {return (UserDetailAuthenticationToken.class.isAssignableFrom(authentication));}}

配置Security

/*** Security配置*/
@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {/*** 业务逻辑bean*/@Autowiredprivate SysUserService sysUserService;/*** redis操作工具类*/@Autowired(required = false)private RedisKeyUtil redisKeyUtil;/*** 密码方式为加密设置*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}/*** 跨域设置*/@BeanCorsConfigurationSource corsConfigurationSource() {final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());return source;}/*** 账号密码认证设置*/@Bean(name = "userDetailsService")public UserDetailsService userDetailsService() {return new UserDetailsServiceImpl(sysUserService);}/*** 手机验证码认证设置*/@Bean(name ="mobileUserDetailsServiceImpl")public UserDetailsService mobileUserDetailsServiceImpl() {return new MobileUserDetailsServiceImpl(sysUserService);}/*** 账号验证码认证提供者设置*/@Beanpublic UserDetailAuthenticationProvider userDetailAuthenticationProvider() {return new UserDetailAuthenticationProvider(userDetailsService(),redisKeyUtil,passwordEncoder());}/*** 手机验证码认证提供者设置*/@Beanpublic MobileCodeAuthenticationProvider mobileCodeAuthenticationProvider() {return new MobileCodeAuthenticationProvider(mobileUserDetailsServiceImpl(),redisKeyUtil);}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//自定义认证处理器//手机号认证auth.authenticationProvider(mobileCodeAuthenticationProvider());//账密认证auth.authenticationProvider(userDetailAuthenticationProvider());}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.cors().and().csrf().disable().authorizeRequests().anyRequest().authenticated().and().addFilter(new JWTAuthenticationFilter(authenticationManager())).addFilter(new JWTAuthorizationFilter(authenticationManager()))// 不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint()).accessDeniedHandler(new JWTAccessDeniedHandler()).and().logout().logoutUrl("/user/logout").logoutSuccessHandler(new JWTLogoutSuccessHandler());}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/auth/**").antMatchers("/api/**").antMatchers("/ws/**").antMatchers("/export/**").antMatchers("/accBalChk/**").antMatchers("/monAcc/**").antMatchers("/doc.html", "/doc.html/**", "/webjars/**", "/v2/**", "/swagger-resources", "/swagger-resources/**", "/swagger-ui.html", "/swagger-ui.html/**");}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/482871.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[CISCN 2019华东南]Web11

[CISCN 2019华东南]Web11 给了两个链接但是都无法访问 这里我们直接抓包试一下 我们插入X-Forwarded-For:127.0.0.1 发现可以修改了右上角的IP地址&#xff0c;从而可以进行注入 {$smarty.version} 查看版本号 if标签执行PHP命令 {if phpinfo()}{/if} 查看协议 {if system(…

使用SpringBoot实现邮件发送(QQ邮箱为例)

使用SpringBoot实现邮件发送(QQ邮箱为例) 一、获取授权码 1.首先进入qq邮箱找到设置 2、账号栏目&#xff0c;找到POP3/SMTP服务 并开启服务 3、获取授权码 二、SpringBoot集成邮件发送 1.创建邮件发送服务类 package com.example.demo.service;import org.springframework…

hint: Updates were rejected because the tip of your current branch is behind!

问题 本地仓库往远段仓库推代码时候提示&#xff1a; error: failed to push some refs to 192.168.2.1:java-base/java-cloud.git hint: Updates were rejected because the tip of your current branch is behind! refs/heads/master:refs/heads/master [rejected] (…

基于BM1684的AI边缘服务器-模型转换,大模型一体机(二)

目标追踪 注&#xff1a;所有模型转换都是在docker环境中的 先进入docker 这里我们是要在docker环境里编译的&#xff0c;所以先进入docker :~/tpu-nntc# docker run -v $PWD/:/workspace -it sophgo/tpuc_dev:latest初始化环境 root2bb02a2e27d5:/workspace/tpu-nntc# s…

ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本)

ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本) code review! 参考笔记 1.ROS基本框架1——编写简单的发布者和订阅者(C++和Python版本) 2.ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本) 文章目录 ROS基本框架2——在ROS开发中创建并使用自定义…

实例讲解MATLAB绘图坐标轴标签旋转

在进行绘图时需要在图片上添加上做标轴的标签&#xff0c;但是当数据量比较多时&#xff0c;例如一天24小时的数据&#xff0c;这时把每个小时显示在左边轴的标签上&#xff0c;文字内容放不下&#xff0c;因此需要将坐标轴标签旋转一定的角度&#xff0c;这样可以更好在图形上…

Spark 内存管理机制

Spark 内存管理 堆内内存和堆外内存 作为一个 JVM 进程&#xff0c;Executor 的内存管理建立在 JVM(最小为六十四分之一&#xff0c;最大为四分之一)的内存管理之上&#xff0c;此外spark还引入了堆外内存&#xff08;不在JVM中的内存&#xff09;&#xff0c;在spark中是指不…

为什么爱用低秩矩阵

目录 为什么爱用低秩矩阵 一、定义与性质 二、区别与例子 为什么爱用低秩矩阵 我们更多地提及低秩分解而非满秩分解,主要是因为低秩分解在数据压缩、噪声去除、模型简化和特征提取等方面具有显著的优势。而满秩分解虽然能够保持数据的完整性,但在实际应用中的场景较为有限…

Dify+Docker

1. 获取代码 直接下载 &#xff08;1&#xff09;访问 langgenius/dify: Dify is an open-source LLM app development platform. Difys intuitive interface combines AI workflow, RAG pipeline, agent capabilities, model management, observability features and more, …

Android Studio的AI工具插件使用介绍

Android Studio的AI工具插件使用介绍 一、前言 Android Studio 的 AI 工具插件具有诸多重要作用&#xff0c;以下是一些常见的方面&#xff1a; 代码生成与自动补全 代码优化与重构 代码解读 学习与知识获取 智能搜索与资源推荐实际使用中可以添加注释&#xff0c;解读某段代…

DOCKER学习总结

这里写目录标题 一、Docker安装1.1 在线安装1.2 离线安装安装配置启动服务 1.3 配置镜像1.4 Docker启动相关命令 二、Docker三大核心概念2.1 镜像2.2 容器2.3 仓库2.3.1 公有仓库2.3.2 私有仓库 二、容器与虚拟机比较 一、Docker安装 1.1 在线安装 查看是否安装dockeryum lis…

深入浅出体验AI生图产品Dall-E

DALL-E是由OpenAI开发的一种革命性的AI图像生成工具&#xff0c;能够根据文本描述生成图像。它的名字灵感来源于著名画家萨尔瓦多达利&#xff08;Salvador Dal&#xff09;和皮克斯动画电影中的角色瓦力&#xff08;WALL-E&#xff09;&#xff0c;这暗示了其在艺术创造力与技…

OpenCV_Code_LOG

孔洞填充 void fillHole(const Mat srcBw, Mat &dstBw) {Size m_Size srcBw.size();Mat TempMat::zeros(m_Size.height2,m_Size.width2,srcBw.type());//延展图像srcBw.copyTo(Temp(Range(1, m_Size.height 1), Range(1, m_Size.width 1)));cv::floodFill(Temp, Point(…

YOLOv11改进,YOLOv11添加SAConv可切换空洞卷积,二次创新C3k2结构

摘要 作者提出的技术结合了递归特征金字塔和可切换空洞卷积,通过强化多尺度特征学习和自适应的空洞卷积,显著提升了目标检测的效果。 理论介绍 空洞卷积(Atrous Convolution)是一种可以在卷积操作中插入“空洞”来扩大感受野的技术,更有效地捕捉到图像中的大范围上下文…

2024信创数据库TOP30之华为Gauss DB

近日&#xff0c;由DBC联合CIW/CIS共同发布的“2024信创数据库TOP30”榜单正式揭晓&#xff0c;汇聚了国内顶尖的数据库企业及其产品&#xff0c;成为展示中国信创领域技术实力与发展潜力的重要平台。在这份榜单中&#xff0c;华为的GaussDB凭借其卓越的技术实力、广泛的行业应…

【Spring源码核心篇-07】spring事物传播机制的流程和原理

Spring源码核心篇整体栏目 内容链接地址【一】Spring的bean的生命周期https://zhenghuisheng.blog.csdn.net/article/details/143441012【二】深入理解spring的依赖注入和属性填充https://zhenghuisheng.blog.csdn.net/article/details/143854482【三】精通spring的aop的底层原…

Redis实现限量优惠券的秒杀

核心&#xff1a;避免超卖问题&#xff0c;保证一人一单 业务逻辑 代码步骤分析 全部代码 Service public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {Resourceprivate ISeckillVoucher…

.NET8/.NETCore 依赖注入:自动注入项目中所有接口和自定义类

.NET8/.NETCore 依赖接口注入&#xff1a;自动注入项目中所有接口和自定义类 目录 自定义依赖接口扩展类&#xff1a;HostExtensions AddInjectionServices方法GlobalAssemblies 全局静态类测试 自定义依赖接口 需要依赖注入的类必须实现以下接口。 C# /// <summary>…

搭建一个基于Web的文档管理系统,用于存储、共享和协作编辑文档

搭建一个基于Web的文档管理系统&#xff0c;用于存储、共享和协作编辑文档 本项目采用以下架构&#xff1a; NFS服务器: 负责存储文档资料。Web服务器: 负责提供文档访问和编辑功能。SELinux: 负责权限控制&#xff0c;确保文档安全。Git服务器: 负责存储文档版本历史&#x…

gitee:创建仓库,存入本地文件至仓库

一、git下载 git:下载与安装-CSDN博客https://blog.csdn.net/weixin_46001736/article/details/144107485?sharetypeblogdetail&sharerId144107485&sharereferPC&sharesourceweixin_46001736&spm1011.2480.3001.8118 二、创建仓库 1、主页面->右上角新增…