从零学习一个基于Springboot的权限管理系统(二)验证码获取

验证码以及注销登录接口

一、生成验证码以及验证码校验

使用hutool的工具类 CodeGenerator生成图片验证码

  1. 配置文件增加Captcha相关配置
  • 包括验证码类型(circle-圆圈干扰验证码 gif-GIT验证码 line-干扰线验证码 shear-扭曲干扰验证码)、
  • 验证码宽度
  • 验证码高度
  • 验证码元素干扰个数
  • 文本透明度(0.0-1.0)
  • 验证码字符配置 类型(math、random) 验证码长度
  • 字体名称 字体大小 验证码有效期
 

yml

代码解读

复制代码

# 验证码配置 captcha: # 验证码类型 circle-圆圈干扰验证码|gif-Gif验证码|line-干扰线验证码|shear-扭曲干扰验证码 type: circle # 验证码宽度 width: 120 # 验证码高度 height: 40 # 验证码干扰元素个数 interfere-count: 2 # 文本透明度(0.0-1.0) text-alpha: 0.8 # 验证码字符配置 code: # 验证码字符类型 math-算术|random-随机字符 type: math # 验证码字符长度,type=算术时,表示运算位数(1:个位数运算 2:十位数运算);type=随机字符时,表示字符个数 length: 1 # 验证码字体 font: # 字体名称 Dialog|DialogInput|Monospaced|Serif|SansSerif name: SansSerif # 字体样式 0-普通|1-粗体|2-斜体 weight: 1 # 字体大小 size: 24 # 验证码有效期(秒) expire-seconds: 120

  1. 使用property类对自定义验证码属性进行读取,声明一个CaptchaProperties类并添加@ConfigurationProperties注解。属性同名
  2. 添加验证码自动装配配置类,根据配置文件验证码类型创建不同的验证码生成对象

整理了这份Java面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处】即可免费获取

java

代码解读

复制代码

@Configuration public class CaptchaConfig { @Autowired private CaptchaProperties captchaProperties; /** * 验证码文字生成器 * * @return CodeGenerator */ @Bean public CodeGenerator codeGenerator() { String codeType = captchaProperties.getCode().getType(); int codeLength = captchaProperties.getCode().getLength(); if ("math".equalsIgnoreCase(codeType)) { return new MathGenerator(codeLength); } else if ("random".equalsIgnoreCase(codeType)) { return new RandomGenerator(codeLength); } else { throw new IllegalArgumentException("Invalid captcha generator type: " + codeType); } } /** * 验证码字体 */ @Bean public Font captchaFont() { String fontName = captchaProperties.getFont().getName(); int fontSize = captchaProperties.getFont().getSize(); int fontWight = captchaProperties.getFont().getWeight(); return new Font(fontName, fontWight, fontSize); } }

  1. 编写业务逻辑 AuthController--->AuthService--->AuthServiceImpl 将CodeGenerator注入到AuthServiceImpl中,根据配置生产出不同的验证码,将生成的CaptchaCode以及对应的Base64编码存储下来。生成验证码key,用以存储到redis中。将key以及base64编码图片发送给前端
 

java

代码解读

复制代码

captcha.setGenerator(codeGenerator); captcha.setTextAlpha(captchaProperties.getTextAlpha()); captcha.setFont(captchaFont); String captchaCode = captcha.getCode(); String imageBase64Data = captcha.getImageBase64Data(); // 验证码文本缓存至Redis,用于登录校验 String captchaKey = IdUtil.fastSimpleUUID(); redisTemplate.opsForValue().set(SecurityConstants.CAPTCHA_CODE_PREFIX + captchaKey, captchaCode, captchaProperties.getExpireSeconds(), TimeUnit.SECONDS); return CaptchaResult.builder() .captchaKey(captchaKey) .captchaBase64(imageBase64Data) .build();

  1. 验证码校验时,根据用户登录时提交上来的captchakey从redis中获取对应的captchacode。与前端提交上来的captchacode进行比对。相等放行,不相等直接给前端返回响应的错误码。

二、Hutool常用的工具类介绍 (官网链接为: doc.hutool.cn/)

类型转换工具类 Convert

基本类型转换

 

java

代码解读

复制代码

int a = 1; //aStr为"1" String aStr = Convert.toStr(a); long[] b = {1,2,3,4,5}; //bStr为:"[1, 2, 3, 4, 5]" String bStr = Convert.toStr(b);

通过convert(TypeReference<T> reference, Object value)方法,自行new一个TypeReference对象可以对嵌套泛型进行类型转换。例如,我们想转换一个对象为List<String>类型,此时传入的标准Class就无法满足要求,此时我们可以这样:

 

ini

代码解读

复制代码

Object[] a = { "a", "你", "好", "", 1 }; List<String> list = Convert.convert(new TypeReference<List<String>>() {}, a);

还包括半角全角转换、16进制、编码转换、金额大小写、时间单位换算等等

日期时间转换类DateUtil以及LocalDateUtil

 

java

代码解读

复制代码

LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56"); // "2020-01-23 12:23:56" String format = LocalDateTimeUtil.format(localDateTime, DatePattern.NORM_DATETIME_PATTERN);

3. 字符串工具类 StrUtil

这个工具的用处类似于Apache Commons Lang (opens new window)中的StringUtil,之所以使用StrUtil而不是使用StringUtil是因为前者更短,而且Str这个简写我想已经深入人心了,大家都知道是字符串的意思。常用的方法例如isBlankisNotBlankisEmptyisNotEmpty

4. 对象工具类 ObjectUtil

ObjectUtil.equal

比较两个对象是否相等,相等需满足以下条件之一:

  1. obj1 == null && obj2 == null
  2. obj1.equals(obj2)
 

java

代码解读

复制代码

Object a = null; Object b = null; // true ObjectUtil.equals(a, b);

ObjectUtil.contains

对象中是否包含元素。

支持的对象类型包括:

  • String
  • Collection
  • Map
  • Iterator
  • Enumeration
  • Array
 

sql

代码解读

复制代码

int[] array = new int[]{1,2,3,4,5}; // true final boolean contains = ObjectUtil.contains(array, 1);

还有常用 isNull 和 isNotNull判断

唯一id工具 IdUtil

  • UUID
  • ObjectId(MongoDB)
  • Snowflake(Twitter)

信息脱敏工具 DesensitizedUtil

在数据处理或清洗中,可能涉及到很多隐私信息的脱敏工作,因此Hutool针对常用的信息封装了一些脱敏方法。

支持的脱敏数据类型包括:

  1. 用户id
  2. 中文姓名
  3. 身份证号
  4. 座机号
  5. 手机号
  6. 地址
  7. 电子邮件
  8. 密码
  9. 中国大陆车牌,包含普通车辆、新能源车辆
  10. 银行卡

整体来说,所谓脱敏就是隐藏掉信息中的一部分关键信息,用*代替,自定义隐藏可以使用StrUtil.hide方法完成 我们以身份证号码为例:

 

java

代码解读

复制代码

// 5***************1X DesensitizedUtil.idCardNum("51343620000320711X", 1, 2);

对于约定俗成的脱敏,我们可以不用指定隐藏位数,比如手机号:

 

java

代码解读

复制代码

// 180****1999 DesensitizedUtil.mobilePhone("18049531999");

当然还有一些简单粗暴的脱敏,比如密码,只保留了位数信息:

 

java

代码解读

复制代码

// ********** DesensitizedUtil.password("1234567890");

断言 Assert类

主要作用是在方法或者任何地方对参数的有效性做校验。当不满足断言条件时,会抛出IllegalArgumentExceptionIllegalStateException异常。

  • isTrue 必须为true,否则抛出IllegalArgumentException异常
  • isNull 必须是null值
  • notNull 不能是null值
  • notEmpty 不能为空,支持字符串,数组,集合等
  • notBlank 不能是空白字符串
  • notContain 不能包含指定的子串
  • noNullElements 数组中不能包含null元素
  • isInstanceOf 必须是指定类的实例
  • isAssignable 必须是子类和父类关系
  • state 会抛出IllegalStateException异常

JsonUtil工具类

JSON字符串创建

JSONUtil.toJsonStr可以将任意对象(Bean、Map、集合等)直接转换为JSON字符串。 如果对象是有序的Map等对象,则转换后的JSON字符串也是有序的。

 

java

代码解读

复制代码

SortedMap<Object, Object> sortedMap = new TreeMap<Object, Object>() { private static final long serialVersionUID = 1L; { put("attributes", "a"); put("b", "b"); put("c", "c"); }}; //{"attributes":"a","b":"b","c":"c"} JSONUtil.toJsonStr(sortedMap);

JSON字符串解析
 

java

代码解读

复制代码

String html = "{"name":"Something must have been changed since you leave"}"; JSONObject jsonObject = JSONUtil.parseObj(html); jsonObject.getStr("name");

JSON转Bean
 

java

代码解读

复制代码

String json = "{"ADT":[[{"BookingCode":["N","N"]}]]}"; Price price = JSONUtil.toBean(json, Price.class);

JWTUtil工具类

  • JWT创建
 

java

代码解读

复制代码

Map<String, Object> map = new HashMap<String, Object>() { private static final long serialVersionUID = 1L; { put("uid", Integer.parseInt("123")); put("expire_time", System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 15); } }; JWTUtil.createToken(map, "1234".getBytes());

  • JWT解析
 

java

代码解读

复制代码

String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." + "U2aQkC2THYV9L0fTN-yBBI7gmo5xhmvMhATtu8v0zEA"; final JWT jwt = JWTUtil.parseToken(rightToken); jwt.getHeader(JWTHeader.TYPE); jwt.getPayload("sub");

  • JWT验证
 

java

代码解读

复制代码

String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2MjQwMDQ4MjIsInVzZXJJZCI6MSwiYXV0aG9yaXRpZXMiOlsiUk9MRV_op5LoibLkuozlj7ciLCJzeXNfbWVudV8xIiwiUk9MRV_op5LoibLkuIDlj7ciLCJzeXNfbWVudV8yIl0sImp0aSI6ImQ0YzVlYjgwLTA5ZTctNGU0ZC1hZTg3LTVkNGI5M2FhNmFiNiIsImNsaWVudF9pZCI6ImhhbmR5LXNob3AifQ." + "aixF1eKlAKS_k3ynFnStE7-IRGiD5YaqznvK2xEjBew"; JWTUtil.verify(token, "123456".getBytes());

三、 用户注销

业务逻辑比较简单,重点是验证token是否过期。如果没过期,需要将该token对应的jwt_id 存储到redis中设置为黑名单。不能再通过该token实现数据鉴权

 

java

代码解读

复制代码

/** * 注销 */ @Override public void logout() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String token = request.getHeader(HttpHeaders.AUTHORIZATION); if (StrUtil.isNotBlank(token) && token.startsWith(SecurityConstants.JWT_TOKEN_PREFIX)) { token = token.substring(SecurityConstants.JWT_TOKEN_PREFIX.length()); // 解析Token以获取有效载荷(payload) JSONObject payloads = JWTUtil.parseToken(token).getPayloads(); // 解析 Token 获取 jti(JWT ID) 和 exp(过期时间) String jti = payloads.getStr(JWTPayload.JWT_ID); Long expiration = payloads.getLong(JWTPayload.EXPIRES_AT); // 过期时间(秒) // 如果exp存在,则计算Token剩余有效时间 if (expiration != null) { long currentTimeSeconds = System.currentTimeMillis() / 1000; if (expiration < currentTimeSeconds) { // Token已过期,不再加入黑名单 return; } // 将Token的jti加入黑名单,并设置剩余有效时间,使其在过期后自动从黑名单移除 long ttl = expiration - currentTimeSeconds; redisTemplate.opsForValue().set(SecurityConstants.BLACKLIST_TOKEN_PREFIX + jti, null, ttl, TimeUnit.SECONDS); } else { // 如果exp不存在,说明Token永不过期,则永久加入黑名单 redisTemplate.opsForValue().set(SecurityConstants.BLACKLIST_TOKEN_PREFIX + jti, null); } } // 清空Spring Security上下文 SecurityContextHolder.clearContext(); }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值