课程项目设计--spring security--用户管理功能--宿舍管理系统--springboot后端

写在前面:
还要实习,每次时间好少呀,进度会比较慢一点
本文主要实现是用户管理相关功能。
前文项目建立

文章目录

  • 验证码功能
    • 验证码配置
    • 验证码生成工具类
    • 添加依赖
    • 功能测试
    • 编写controller接口
    • 启动项目
  • security配置
    • 拦截器配置
      • 验证码拦截器
    • jwt拦截器
    • 思考
  • 用户登录
    • jwt管理
    • 验证
  • 用户注销

验证码功能

验证码采用的是hutool工具的验证码
hutool官方地址

工具模板采用有来开源组织

验证码配置

yml配置

CaptchaConfig:#  验证码缓存过期时间(单位:秒)ttl: 120l# 验证码内容长度length: 4# 验证码宽度width: 120# 验证码高度height: 40# 验证码字体font-name: Verdana# 验证码字体大小fontSize: 20

配置类

/*** EasyCaptcha 配置类* * @author haoxr* @since 2023/03/24*/
@ConfigurationProperties(prefix = "easy-captcha")
@Configuration
@Data
public class CaptchaConfig {// 验证码类型private CaptchaTypeEnum type = CaptchaTypeEnum.ARITHMETIC;// 验证码缓存过期时间(单位:秒)@Value("${captcha.ttl}")private long ttl;// 内容长度@Value("${captcha.length}")private int length;// 宽度@Value("${captcha.width}")private int width;// 验证码高度@Value("${captcha.height}")private int height;// 验证码字体@Value("${captcha.font-name}")private String fontName;// 字体风格private Integer fontStyle = Font.PLAIN;// 字体大小@Value("${captcha.font-size}")private int fontSize;}

验证码生成工具类

@Component
@RequiredArgsConstructor
public class EasyCaptchaProducer {private final CaptchaConfig captchaConfig;public Captcha getCaptcha() {Captcha captcha;int width = captchaConfig.getWidth();int height = captchaConfig.getHeight();int length = captchaConfig.getLength();String fontName = captchaConfig.getFontName();switch (captchaConfig.getType()) {case ARITHMETIC -> {captcha = new ArithmeticCaptcha(width, height);captcha.setLen(2);}case CHINESE -> {captcha = new ChineseCaptcha(width, height);captcha.setLen(length);}case CHINESE_GIF -> {captcha = new ChineseGifCaptcha(width, height);captcha.setLen(length);}case GIF -> {captcha = new GifCaptcha(width, height);//最后一位是位数captcha.setLen(length);}case SPEC -> {captcha = new SpecCaptcha(width, height);captcha.setLen(length);}default -> throw new RuntimeException("验证码配置信息错误!正确配置查看 CaptchaTypeEnum ");}captcha.setFont(new Font(fontName, captchaConfig.getFontStyle(), captchaConfig.getFontSize()));return captcha;}}

添加依赖

        <!-- Java8 之后JavaScript引擎nashorn被移除导致验证码解析报错--><dependency><groupId>org.openjdk.nashorn</groupId><artifactId>nashorn-core</artifactId><version>${nashorn.version}</version></dependency>

功能测试

        Captcha captcha = easyCaptchaProducer.getCaptcha();try (OutputStream ops = new FileOutputStream("d://captcha.jpg")) {captcha.out(ops);} catch (Exception e) {e.printStackTrace();}System.out.println(captcha.text());

测试结果
在这里插入图片描述
在这里插入图片描述

编写controller接口

@Tag(name = "01-认证中心")
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
@Slf4j
public class AuthController {private final EasyCaptchaService easyCaptchaService;@Operation(summary = "获取验证码")@GetMapping("/captcha")public Result<CaptchaResult> getCaptcha() {CaptchaResult captcha = easyCaptchaService.getCaptcha();return Result.success(captcha);}
}

启动项目

记住这里,这是你spring security 的密码
在这里插入图片描述

生成http

通过base64转图片的在线工具可以看到
在这里插入图片描述
说明编写成功了。

security配置

在上面我们默认的是spring security 自动的密码。我们现在需要自己设置密码。

spring security 框架捏,不太好说这玩意。挺忘记了。
不过spring boot3使用的是spring security6.0版本和以前的有很大差别,6.0通过配置bean来进行。所以也还好,反正都是从头学。
首先需要配置security的配置类

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {// 密码编码器@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}/*** 不走过滤器链的放行配置* 默认放行静态资源、登录接口、验证码接口、Swagger接口文档*/@Beanpublic WebSecurityCustomizer webSecurityCustomizer() {return (web) -> web.ignoring().requestMatchers("/auth/captcha","/webjars/**","/doc.html","/swagger-resources/**","/swagger-ui/**","/ws/**");}
}/*** 认证管理器** @param authenticationConfiguration 认证配置* @return 认证管理器* @throws Exception 异常*/@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {return authenticationConfiguration.getAuthenticationManager();}@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(requestMatcherRegistry ->requestMatcherRegistry.requestMatchers(SecurityConstants.LOGIN_PATH).permitAll().anyRequest().authenticated()).sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)).exceptionHandling(httpSecurityExceptionHandlingConfigurer ->httpSecurityExceptionHandlingConfigurer.authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler)).csrf(AbstractHttpConfigurer::disable);// 验证码校验过滤器http.addFilterBefore(new VerifyCodeFilter(), UsernamePasswordAuthenticationFilter.class);// JWT 校验过滤器http.addFilterBefore(new JwtAuthenticationFilter(jwtTokenManager), UsernamePasswordAuthenticationFilter.class);return http.build();}

这里还用到了2个拦截器

拦截器配置

验证码拦截器

需求:对登录请求进行拦截,如果是登录则需要先校验验证码是否正常,如果正确则放行。其他请求则直接放行。

public class VerifyCodeFilter extends OncePerRequestFilter {private static final AntPathRequestMatcher LOGIN_PATH_REQUEST_MATCHER = new AntPathRequestMatcher(SecurityConstants.LOGIN_PATH, "POST");public static final String VERIFY_CODE_PARAM_KEY = "verifyCode";public static final String VERIFY_CODE_KEY_PARAM_KEY = "verifyCodeKey";@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 如果是登录请求则校验验证码if (LOGIN_PATH_REQUEST_MATCHER.matches(request)){String code = request.getParameter(VERIFY_CODE_PARAM_KEY);String verifyCodeKey = request.getParameter(VERIFY_CODE_KEY_PARAM_KEY);// 由于这个不是bean,不能通过注入的方式获取,所以通过SpringUtil工具类获取RedisTemplate redisTemplate = SpringUtil.getBean("redisTemplate", RedisTemplate.class);String cacheCode =  Convert.toStr(redisTemplate.opsForValue().get(SecurityConstants.VERIFY_CODE_CACHE_PREFIX + verifyCodeKey));if (cacheCode == null) {// 验证码过期ResponseUtils.writeErrMsg(response, ResultCode.VERIFY_CODE_TIMEOUT);return;}if (!StrUtil.equals(cacheCode,code)) {// 验证码错误ResponseUtils.writeErrMsg(response, ResultCode.VERIFY_CODE_ERROR);return;}}filterChain.doFilter(request, response);}
}

jwt拦截器

需求:处理登录请求以外的请求,每次需要验证jwt令牌,如果没问题则在该线程请求附加权限身份。

public class JwtAuthenticationFilter extends OncePerRequestFilter {private static final AntPathRequestMatcher LOGIN_PATH_REQUEST_MATCHER = new AntPathRequestMatcher(SecurityConstants.LOGIN_PATH, "POST");private final JwtTokenManager tokenManager;public JwtAuthenticationFilter(JwtTokenManager jwtTokenManager) {this.tokenManager = jwtTokenManager;}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {if (!LOGIN_PATH_REQUEST_MATCHER.matches(request)) {String jwt = RequestUtils.resolveToken(request);if (StringUtils.hasText(jwt) && SecurityContextHolder.getContext().getAuthentication() == null) {try {Claims claims = this.tokenManager.parseAndValidateToken(jwt);Authentication authentication = this.tokenManager.getAuthentication(claims);SecurityContextHolder.getContext().setAuthentication(authentication);} catch (Exception e) {ResponseUtils.writeErrMsg(response, ResultCode.TOKEN_INVALID);}} else {ResponseUtils.writeErrMsg(response, ResultCode.TOKEN_INVALID);}}chain.doFilter(request, response);}
}

思考

这2个拦截器一个需要登录一个除去登录,那么是不是可以放到一个拦截器里面去。各走各的。这样也明确一点。也不用2个拦截器找了。

如果改了记得改securityFilterChain

用户登录

需求:输入用户名和密码,验证用户身份。
需要写一个类继承UserDetails
在这里插入图片描述

另一个实现类继承SysUserService(SysUserDetailsService)
在这里插入图片描述
这2个一个是存储一个是查询。然后会自动和输入的username以及password进行比对
验证流程后面总结一个spring security的文。

SysUserDetailsService作用是查询该用户名的角色信息并返回UserDetails。

查询,调用SysUserService根据用户名查询所有的
在这里插入图片描述
由于认证信息需要角色信息和权限所以我们需要联表查询角色信息。
在依据角色信息查询权限。

        select u.id userId,u.name username,u.password,u.role,u.avatar,u.email,u.status,r.codefrom sys_user uleft join sys_user_role sur on u.id = sur.user_idleft join sys_role r on sur.role_id = r.idwhere u.name = #{username}AND u.deleted = 0

然后在依据角色查询权限
不过我感觉这个type硬编码挺严重的,也算学习一下这种mybatis里面枚举了。
如果没用角色则m.id = -1让其没权限。

<select id="listRolePerms" resultType="java.lang.String">select distinct m.permfrom sys_menu minner join sys_role_menu rm on m.id = rm.menu_idinner join sys_role r on r.id = rm.role_idwhere m.type = '${@com.yu.common.enums.MenuTypeEnum@BUTTON.getValue()}'and m.perm is not null<choose><when test="roles!=null and roles.size()>0">and r.code in<foreach collection="roles" item="role" open="(" close=")" separator=",">#{role}</foreach></when><otherwise>and m.id = -1</otherwise></choose></select>

controller验证,很明确的流程就是封装输入的,然后进行验证。失败了会报错返回。
成功则生成token将权限放入redis,将角色,用户名,id封装进jwt
然后进行返回。接下来查看jwtTokenManager.createToken

    @Operation(summary = "登录")@PostMapping("/login")public Result<LoginResult> login(@Parameter(description = "用户名", example = "admin") @RequestParam String username,@Parameter(description = "密码", example = "123456") @RequestParam String password) {// 存储username和passwordUsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username.toLowerCase().trim(),password);// 验证用户名和密码Authentication authentication = authenticationManager.authenticate(authenticationToken);// 生成tokenString accessToken = jwtTokenManager.createToken(authentication);// 返回tokenLoginResult loginResult = LoginResult.builder().tokenType("Bearer").accessToken(accessToken).build();return Result.success(loginResult);}@Schema(description ="登录响应对象")@Builderpublic static record LoginResult(@Schema(description = "访问token")String accessToken,@Schema(description = "token 类型",example = "Bearer")String tokenType,@Schema(description = "刷新token")String refreshToken,@Schema(description = "过期时间(单位:毫秒)")Long expires) {}

jwt管理

采用hutool工具包进行jwt管理,以前用过java-jwt的,这次试试hutool。

    /*** 创建token** @param authentication auth info* @return token*/public String createToken(Authentication authentication) {SysUserDetails userDetails = (SysUserDetails) authentication.getPrincipal();// 角色放入JWT的claimsSet<String> roles = userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet());// 权限数据多放入RedisSet<String> perms = userDetails.getPerms();redisTemplate.opsForValue().set(SecurityConstants.USER_PERMS_CACHE_PREFIX + userDetails.getUserId(), perms);Map<String, Object> claims = Map.of(JWTPayload.ISSUED_AT, DateTime.now(),JWTPayload.EXPIRES_AT, DateTime.now().offset(DateField.SECOND, tokenTtl),"jti", IdUtil.fastSimpleUUID(),"userId", userDetails.getUserId(),"username", userDetails.getUsername(),"authorities", roles);return JWTUtil.createToken(claims, getSecretKeyBytes());}

验证

http测试:
之前测试挺头疼的。
需要先发送验证码的。
然后去base64转图片(后面直接打印了结果了)
进行测试
在这里插入图片描述
成功
在这里插入图片描述
后面去vue3前端测了。用的是有来开源vue3-element-admin修改。
成功了!
在这里插入图片描述

用户注销

待续

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

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

相关文章

电脑上安装,多版本node

手上有一个vue3的项目&#xff0c;sass配置如下图所示&#xff1a; 安装了Python3.10和node 16.14.0&#xff0c;项目能正常install 跟run。 因工作需要&#xff0c;收上有一个vue2的项目&#xff0c;sass配置如下图所示&#xff1a; 执行npm intsall 的时候一直报Python2找不…

2022年12月 C/C++(二级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题:数组逆序重放 将一个数组中的值按逆序重新存放。例如,原来的顺序为8,6,5,4,1。要求改为1,4,5,6,8。 输入 输入为两行:第一行数组中元素的个数n(1<n<100),第二行是n个整数,每两个整数之间用空格分隔。 输出 输出为一行:输出逆序后数组的整数,每两个整数之间…

Java云原生框架Quarkus初探

Java云原生框架Quarkus初探 Quarkus 介绍 Quarkus 是一个云原生&#xff0c;容器优先的Java应用框架&#xff0c;它号称是超音速和亚原子的框架&#xff0c;主要特点是构建速度、启动速度快和占用资源少等特点。它为OpenJDK HotSpot和GraalVM量身定制&#xff0c; 根据Java库和…

常用消息中间件介绍

RocketMQ 阿里开源&#xff0c;阿里参照kafka设计的&#xff0c;Java实现 能够保证严格的消息顺序 提供针对消息的过滤功能 提供丰富的消息拉取模式 高效的订阅者水平扩展能力 实时的消息订阅机制 亿级消息堆积能力 RabbitMQ Erlang实现&#xff0c;非常重量级&#xff0c;更适…

Nevron 3DChart Crack,可视化界面在运行时可用

Nevron 3DChart Crack,可视化界面在运行时可用 3DChart使用OpenGL 3D图形引擎创建复杂的2D和3D图表&#xff0c;这些图表可以包含静态或动画图像。3DChart包括一个用于生成图表模板的独立应用程序和一个ASP服务器配置实用程序。该组件还包括一个专门设计用于与3DChart集成的工具…

java版本企业电子招标采购系统源码Spring Cloud + Spring Boot +二次开发

java版本企业电子招标采购系统源码Spring Cloud Spring Boot 二次开发 一、立项管理 1、招标立项申请 功能点&#xff1a;招标类项目立项申请入口&#xff0c;用户可以保存为草稿&#xff0c;提交。 2、非招标立项申请 功能点&#xff1a;非招标立项申请入口、用户可以保存为草…

Scratch 游戏 之 随机大地图生成教程

在很多生存 / 沙盒类游戏中&#xff0c;地图往往是随机生成的&#xff0c;例如&#xff1a;饥荒、我的世界等。那我们该如何在scratch中实现这一点呢&#xff1f; 在scratch中有两种办法可以实现——画笔和克隆体。我们这次先聊克隆体。 我们可以先将克隆体设置为方形的&#x…

打怪升级之从零开始的网络协议

序言 三个多月过去了&#xff0c;我又来写博客了&#xff0c;这一次从零开始学习网络协议。 总的来说&#xff0c;计算机网络很像现实生活中的快递网络&#xff0c;其最核心的目标&#xff0c;就是把一个包裹&#xff08;信息&#xff09;从A点发送到B点去。下面是一些共同的…

Linux学习之ssh和scp

ls /etc/ssh可以看到这个目录下有一些文件&#xff0c;而/etc/ssh/ssh_config是客户端配置文件&#xff0c;/etc/ssh/sshd_config是服务端配置文件。 cat -n /etc/ssh/sshd_config | grep "Port "可以看一下sshd监听端口的配置信息&#xff0c;发现这个配置端口是22…

git分支

一、引言 分支的命名规范以及管理方式对项目的版本发布至关重要&#xff0c;为了解决实际开发过程中版本发布时代码管理混乱、冲突等比较头疼的问题&#xff0c;我们将在文中阐述如何更好的管理代码分支。 二、总览&#xfeff; 从上图可以看到主要包含下面几个分支&#xff…

【是C++,不是C艹】 手把手带你实现Date类(附源码)

&#x1f49e;&#x1f49e;欢迎来到 Claffic 的博客&#x1f49e;&#x1f49e; &#x1f449; 专栏&#xff1a;《是C&#xff0c;不是C艹》&#x1f448; 前言&#xff1a; 恍惚间&#xff0c;已经两个月没更新了 &#xff08;&#xff1b;д&#xff40;&#xff09;ゞ 我忏…

Redis 持久化的手段有哪些 ?RDB 和 AOF 有什么区别 ?

目录 1. Redis 持久化的手段有哪些 2. RDB 和 AOF 有什么区别 2.1 RDB 持久化 2.2 AOF 持久化 2.2.1 AOF 持久化策略有哪些 3. 混合持久化是如何执行的&#xff08;了解&#xff09; 1. Redis 持久化的手段有哪些 Redis 持久化的手段有三种&#xff1a; 快照方式&#…

开源数据库Mysql_DBA运维实战 (总结)

开源数据库Mysql_DBA运维实战 &#xff08;总结&#xff09; SQL语句都包含哪些类型 DDL DCL DML DQL Yum 安装MySQL的配置文件 配置文件&#xff1a;/etc/my.cnf日志目录&#xff1a;/var/log/mysqld.log错误日志&#xff1a;/var/log/mysql/error.log MySQL的主从切换 查看主…

什么是程序化交易接口?执行三步曲是什么?

在股市中的发展过程中&#xff0c;通过不断的更新迭代&#xff0c;从手动交易到自动交易的过程就有了历史的蜕变&#xff0c;那么对于程序化交易接口&#xff08;Application Programming Interface, API&#xff09;其实就是指为程序化交易提供的一组定义和规范&#xff0c;允…

物联网工程应用实训室建设方案

一、物联网工程应用系统概述 1.1物联网工程定义 物联网工程&#xff08;Internet of Things Engineering&#xff09;是一种以信息技术&#xff08;IT&#xff09;来改善实体世界中人们生活方式的新兴学科&#xff0c;它利用互联网技术为我们的日常生活活动提供服务和增益&am…

时序预测 | MATLAB实现基于CNN-LSTM卷积长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价)

时序预测 | MATLAB实现基于CNN-LSTM卷积长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价) 目录 时序预测 | MATLAB实现基于CNN-LSTM卷积长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价)预测结果基本介绍程序设计参考资料 预测结果 基本介绍 MATLAB实现基…

线程面试题-1

看的博客里面总结的线程的八股文 1、线程安全的集合有哪些&#xff1f;线程不安全的呢&#xff1f; 线程安全的&#xff1a; Hashtable&#xff1a;比HashMap多了个线程安全。 ConcurrentHashMap:是一种高效但是线程安全的集合。 Vector&#xff1a;比Arraylist多了个同步化…

安装Vue_dev_tools

Vue控制台出现Download the Vue Devtools extension for a better development experience: 下载Vue_dev_tools,这里给出网盘链接&#xff0c;有Vue2和Vue3的&#xff0c;dev_tools 以Google浏览器为例 点击设置&#xff08;就是那三个点&#xff09;->扩展程序->管理扩…

游戏服务端性能测试

导语&#xff1a;近期经历了一系列的性能测试&#xff0c;涵盖了Web服务器和游戏服务器的领域。在这篇文章中&#xff0c;我将会对游戏服务端所做的测试进行详细整理和记录。需要注意的是&#xff0c;本文着重于记录&#xff0c;而并非深入的编程讨论。在这里&#xff0c;我将与…

C# Windows登录界面进行截图,控制鼠标键盘等操作实现(一)

首先常规的账户进程是没办法获取登录界面的信息的&#xff0c;因为登录界面已经不在某个账户下了&#xff0c;登录界面显示了每一个账户的切换。所以得使用System权限的进程。 那么Windows系统究竟是怎么将登录界面与用户桌面隔离开的呢&#xff1f;首先先通过一些Windows操作系…