当前位置: 首页 > news >正文

SpringBoot整合SpringSecurity-元一软件

先说一下SpringSecurity是干什么的,SpringSecurity主要作用有2方面:认证、授权。

  • 认证:Authentication, 用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的身份信息,身份合法方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登录,二维码登录,手机短信登录,指纹认证等方式。
  • 授权:Authorize,授权是用户认证通过根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问

权限管理涉及到几个概念:

  • 主体(用户id、账号、密码、…)
  • 资源(资源id、资源名称、访问地址、…)
  • 权限(权限id、权限标识、权限名称、资源id、…)
  • 角色(角色id、角色名称、…)

业界通常基于RBAC实现授权。
在单体应用中,我觉得理解为基于角色的访问控制(Role-Based Access Control)是比较合适的,用起来比较方便。
而在当前动辄微服务开发的环境下,个人觉得理解为基于资源的访问控制(Resource-Based Access Control)用起来更方便,因为微服务中各个微服务都当做资源来看待了。
整合
SpringBoot整合SpringSecurity还是比较简单的:

  1. 引入相关jar包
  2. 配置Security(配置时会稍麻烦,因为需要理解的比较多)

1. 引入Jar包

比较简单,引入web包和security包就行

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>

2. 启动测试

引入jar包后就可以启动了,启动时会生成随机密码:

随机密码

  • 访问项目就会跳转到登陆页面,默认账号:user

    登陆

  • SpringSecurity自带注销地址:/logout,访问这个地址会弹出注销页面。

    注销

3. 自定义配置

以上是SpringSecurity自带的认证功能,我们使用时需要根据我们自己的需要自定义一些内容(2方面配置:认证配置,授权配置),例如:

  • 登陆的账号密码
  • 是否允许表单登陆
  • 密码加密的情况
  • 权限鉴定
3.1 认证配置

自定义的配置其实都在同一个类中,认证和授权在不同的方法中,配置类继承WebSecurityConfigurerAdapter类,重写2个方法就行。
注意,就是这个父类,想要配置什么,点进源码去里面找对应的方法
认证的方式有2种:一是账号密码等认证信息写在配置中,二是账号密码等信息从数据库读取。使用时,取其一

3.1.1 认证信息写在配置中
@Configuration@EnableWebSecuritypublicclassSecurityConfigextendsWebSecurityConfigurerAdapter{/** * 1. 认证配置 */@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth)throwsException{// 测试用的,写死的账号密码auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder().encode("123456")).roles("ADMIN").authorities("/test/t1").and().withUser("user").password(passwordEncoder().encode("123456")).roles("USER").authorities("/test/t2");}// 设置密码加密方法@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}/** * 2. 授权的配置方法,下面再讲,先空着 */@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{}}
3.1.2 认证信息从数据库拿

这种方式,配置类简单,但是需要一个用户服务类,来返回一个SpringSecurity封装的一个user对象,直接将服务类放到配置文件中就行。

importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.authentication.AuthenticationManager;importorg.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.EnableWebSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;@Configuration@EnableWebSecuritypublicclassSecurityConfigextendsWebSecurityConfigurerAdapter{// 注入服务类@AutowiredprivateUserDetailsServiceImpluserDetailsServiceImpl;/** * 1. 认证配置 */@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth)throwsException{auth.userDetailsService(userDetailsServiceImpl);}// 设置密码加密方法,必须设置@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}/** * 2. 授权的配置方法,下面再讲,先空着 */@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{}}

看一下这个服务类怎么写的,先准备一个服务类要返回的UserDetails对象

  • 自定义user实体对象
packagecom.example.demo.security.userdetails;importjava.util.Collection;importjava.util.Date;importorg.springframework.format.annotation.DateTimeFormat;importorg.springframework.security.core.GrantedAuthority;importorg.springframework.security.core.authority.AuthorityUtils;importorg.springframework.security.core.authority.SimpleGrantedAuthority;importorg.springframework.security.core.userdetails.UserDetails;importcom.fasterxml.jackson.annotation.JsonFormat;importlombok.Data;/** * 参考{@link org.springframework.security.core.userdetails.User}这个类, * 这个类是security设置实体类参数值的时候用的,里面很多方法可以参考使用。 * 比如设置roles和设置authorities的过程,在User类的内部类UserBuilder中 */@DatapublicclassMyUserDetail implements UserDetails{privatestaticfinallong serialVersionUID=1L;privateLong userId;privateString username;privateString name;privateString password;privateboolean status;privateLong deptId;privateString email;privateString mobile;privateString sex;privateString avatar;@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")privateDate lastLoginTime;// 角色权限:SpringSecurity中角色和权限都是放在这个里面的,使用起来是一样的,区别在于,角色要加前缀 ROLE_privateCollection<GrantedAuthority>authorities;/** * 参考{@link org.springframework.security.core.userdetails.User.UserBuilder} * 中的roles方法 * @param roles * @return */publicList<GrantedAuthority>roles(String...roles){List<GrantedAuthority>authorities=new ArrayList<>(roles.length);for(String role:roles){Assert.isTrue(!role.startsWith("ROLE_"),()->role+" cannot start with ROLE_ (it is automatically added)");authorities.add(newSimpleGrantedAuthority("ROLE_"+role));}returnauthorities;}/** * 参考{@link org.springframework.security.core.userdetails.User.UserBuilder} * 中的 authorities 方法 * @param authorities * @return */publicList<GrantedAuthority>authorities(String authorities){returnAuthorityUtils.commaSeparatedStringToAuthorityList(authorities);}publicList<GrantedAuthority>authorities(String...authorities){returnAuthorityUtils.createAuthorityList(authorities);}@OverridepublicCollection<?extends GrantedAuthority>getAuthorities(){returnthis.authorities;}@OverridepublicbooleanisAccountNonExpired(){// 先按这个判断,需要什么自己添加returnthis.isStatus();}@OverridepublicbooleanisAccountNonLocked(){returnthis.isStatus();}@OverridepublicbooleanisCredentialsNonExpired(){returnthis.isStatus();}@OverridepublicbooleanisEnabled(){returnthis.isStatus();}}
  • 用户服务类
package com.example.demo.security.userdetails;importjava.util.ArrayList;importjava.util.List;importjavax.annotation.Resource;importorg.springframework.security.authentication.DisabledException;importorg.springframework.security.authentication.LockedException;importorg.springframework.security.core.authority.SimpleGrantedAuthority;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importcom.example.demo.dao.MyUserDao;publicclassUserDetailsServiceImplimplementsUserDetailsService{@ResourceprivateMyUserDaomyUserDao;@OverridepublicUserDetailsloadUserByUsername(Stringusername)throwsUsernameNotFoundException{MyUserDetailuserDetail=myUserDao.getMyUserDetail(username);// 注意数据库中保存的密码,要是加密过的,就是在配置类中设置的加密方法if(!userDetail.isEnabled()){thrownewDisabledException("账号状态异常!");}elseif(!userDetail.isCredentialsNonExpired()){thrownewLockedException("密码过期!");}// 模拟一点角色权限信息,角色前面要加 ROLE_ 前缀userDetail.setAuthorities(userDetail.authorities("/test/t1","/test/t2","ROLE_ADMIN","ROLE_ROOT"));returnuserDetail;}}

使用上面这2种方法之一,我们就可以用我们自己的账号和密码登陆了。

3.1.3 看看SpringSecurity自带的

上面是我们自己实现的接口,写了过程。其实,SpringSecurity自己也封装了很多,我们也可以看看。

实现类

官方包里面的就是用户的实现过程,其实我们可以用自带的这些,但是限制比较多,拿jdbc这个来说,他也重写了loadUsersByUsername()

JdbcUserDetailsManager

但是它限制了很多东西,表名、字段等要符合人家要求:你要有users表,表中要包含这些字段:

sql

如果你要想使用,初始化时传入DataSource即可,他会根据你传入的数据源自动查找数据。

构造方法

3.2 授权配置

**注意:**前提条件是,认证时,账号信息中加入了角色和权限的一些信息,这里才能进行权限判定。
授权配置常用的有2种方式,一是在SecurityConfig类中,一是用注解表达式。

3.2.1 在SecurityConfig类配置权限

授权配置还是在上面的SecurityConfig类中,只不过是在下面的那个方法中配置。

packagecom.example.demo.security;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.EnableWebSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;importcom.example.demo.entity.Result;importcom.example.demo.security.userdetails.UserDetailsServiceImpl;importcn.hutool.json.JSONUtil;/** * SpringSecurity配置 */@Configuration@EnableWebSecuritypublicclassSecurityConfigextendsWebSecurityConfigurerAdapter{// 注入服务类@AutowiredprivateUserDetailsServiceImpluserDetailsServiceImpl;/** * 1. 认证配置 */@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth)throwsException{auth.userDetailsService(userDetailsServiceImpl);}// 设置密码加密方法@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}/** * 2. 授权的配置方法,下面再讲,先空着 */@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{// 1. 登陆登出设置http// 允许表单登陆.formLogin()// 自定义登陆页面,注意action提交地址 和 账号密码表单name// .loginPage("/login.html")// 自定义后端登陆地址,security默认的是/login// .loginProcessingUrl("/doLogin")// 自定义登陆成功后的处理,前后端分离一般返回json数据.successHandler(newMyAuthenticationSuccessHandler())// 自定义登陆失败后的处理,前后端分离一般返回json数据.failureHandler(newMyAuthenticationFailureHandler()).and().logout()// 自定义退出地址// .logoutUrl("/logout")// 退出成功后的处理.logoutSuccessHandler((req,res,aut)->{res.setContentType("application/json;charset=utf-8");Result<String>result=newResult<>();result.setStatus(1);result.setCode("200");result.setMsg("退出成功");res.getWriter().write(JSONUtil.toJsonStr(result));})//使得session失效,默认true// .invalidateHttpSession(true)//清除认证信息,默认true// .clearAuthentication(true)//删除指定的cookie// .deleteCookies("cookie01");// 2. 跨域问题http.csrf().disable();// 3. 权限设置http// 对url进行访问权限控制.authorizeRequests()// 按角色来控制权限的.antMatchers("/test/t2").hasRole("ADMIN").antMatchers("/admin1/**","/admin2/**").hasAnyRole("ADMIN1","ADMIN2")// 按Authority,有权限才能访问.antMatchers("/user/**").hasAuthority("/u/a").antMatchers("/test/t1").hasAuthority("/test/t1")// 直接放行的.antMatchers("/app/**").permitAll()// 其他任何请求都需要登陆.anyRequest().authenticated();}}
3.2.2 注解表达式配置权限

SpringSecurity的权限注解有5个,都是用在方法上的,分别是:

  • @Secured:检查指定的角色权限,角色要加前缀ROLE_,可以多个,如:@Secured({"ROLE_A", "ROLE_B"})
  • @PreAuthorize:方法执行前进行权限检查,一般都是用这个。用法:@PreAuthorize("hasRole('admin')")@PreAuthorize("hasAuthority('/t1') and hasAuthority('/t2')")@PreAuthorize("hasAnyRole('root','admin')")
  • @PostAuthorize:方法执行后进行权限检查,还没用过
  • @PreFilter:过滤函数,未用过
  • @PostFilter:过滤函数,未用过

注意:

  • 要想使用注解,需要开启注解,在security的配置类加上@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)

    开启注解

  • 注释掉配置类方法中,关于权限的配置。

注解表达式使用如下:

packagecom.example.demo.controller;importjavax.servlet.http.HttpServletResponse;importorg.springframework.security.access.prepost.PreAuthorize;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/test")publicclassTestController{@GetMapping("/t1")publicStringtest1(String name,HttpServletResponse response){response.addHeader("userId","123");returnname==null?"zhangsan":name;}@GetMapping("/t2")@PreAuthorize("hasAnyRole('ROOT','ADMIN')")publicStringtest2(){return"test2";}@GetMapping("/t3")@PreAuthorize("hasAuthority('/test/t3')")publicStringtest3(){return"test3";}@GetMapping("/t4")@PreAuthorize("hasAuthority('/t4') and hasAuthority('/t5')")publicStringtest4(){return"test4";}@GetMapping("/t5")@PreAuthorize("hasRole('admin')")publicStringtest5(){return"test5";}}

注解的判断方法走的是类org.springframework.security.access.expression.SecurityExpressionRoot中的,可以看看逻辑。

http://www.jsqmd.com/news/1191848/

相关文章:

  • 机器学习系列:随机采样方法之接受-拒绝采样
  • 深入解析TI TPS929240-Q1:24通道LED驱动芯片的精密调光与汽车级诊断保护
  • 2026 年新消息:兰山诚信的镀锌无缝钢管实力厂家哪个好,别信“防锈王者”的鬼话,这玩意儿半年就锈穿工地,太坑人! - 行业严选官
  • AD74413R与PIC18F45K42实现高精度模拟信号采集与输出
  • 内网环境下的O2OA与OnlyOffice集成:打造安全高效的Excel协同办公平台
  • 亲身探访广州宝玑官方售后服务中心|完整地址与售后服务热线(2026年7月最新) - 亨得利钟表维修中心
  • 卡地亚中国官方售后服务中心|服务电话及全部网点地址权威信息公告(2026年7月更新) - 卡地亚官方售后中心
  • 空间权重矩阵构建实战:从理论到GIS与Stata实现
  • 【项目编号:project42742】|SpringBoot中医健康养生信息管理系统:养生资讯、体质调理与后台内容运营技术栈:SpringBoot + MySQL
  • CandyHandymanPlatform
  • 2026年7月侧柏基地推荐,侧柏小苗/黑松盆景/黑松容器苗/绿化造型松/侧柏土球/黑松小苗/黑松,侧柏种植基地哪家强 - 品牌推荐师
  • Docker部署ClickHouse时序数据库
  • KCOM4串口转键鼠控制线实战应用与固件升级指南
  • Spring Cloud Alibaba 2022集成Nacos
  • RK3588 HDMI-IN功能实战:从设备树配置到应用调试全解析
  • 手搓生产级 AI Agent 系统(2):工具系统深度实战——从参数校验到沙箱安全
  • Unity XLua热更新报错:Span与ReadOnlySpan类型缺失的深度解析与解决方案
  • GCC警告与优化协同配置:解决C++发布版本崩溃难题
  • Claude Code到底能不能干活?别只看 Demo 和跑分
  • 2026年7月最新南宁卡地亚官方售后维修服务网点地址与客服电话 - 卡地亚服务中心
  • Claude Code高效协作指南:claude.md编写规范与工程实践
  • 2026加密算法全景解析:从AES到后量子密码的实战指南
  • 零代码自动化:从重复操作到AI增强工作流的实践指南
  • 2026年苏州线切割设备靠谱生产服务商介绍:线切割机、放电切割机、阳极切割机设备制造企业 - 海棠依旧大
  • Armadillo预编译版在Windows+VS2022下的实战配置与性能优化
  • SpringCloud生态下的新选择:深入剖析PowerJob的分布式调度核心机制
  • STM32(三):外部中断实战避坑指南(标准库函数)
  • SpringBoot+Vue实战:从零构建一个课堂管理系统
  • 可行性研究实战:从经典习题到现代系统分析的思维跃迁
  • Unreal Engine模块加载失败全解析:从原理到排查的完整指南