1.md5加密密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Md5Util {
/**
* 默认的密码字符串组合,用来将字节转换成 16 进制表示的字符,apache校验下载的文件的正确性用的就是默认的这个组合
*/
protected static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

protected static MessageDigest messagedigest = null;

static {
try {
messagedigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nsaex) {
System.err.println(Md5Util.class.getName() + "初始化失败,MessageDigest不支持MD5Util。");
nsaex.printStackTrace();
}
}

/**
* 生成字符串的md5校验值
*
* @param s
* @return
*/
public static String getMD5String(String s) {
return getMD5String(s.getBytes());
}

/**
* 判断字符串的md5校验码是否与一个已知的md5码相匹配
*
* @param password 要校验的字符串
* @param md5PwdStr 已知的md5校验码
* @return
*/
public static boolean checkPassword(String password, String md5PwdStr) {
String s = getMD5String(password);
return s.equals(md5PwdStr);
}


public static String getMD5String(byte[] bytes) {
messagedigest.update(bytes);
return bufferToHex(messagedigest.digest());
}

private static String bufferToHex(byte bytes[]) {
return bufferToHex(bytes, 0, bytes.length);
}

private static String bufferToHex(byte bytes[], int m, int n) {
StringBuffer stringbuffer = new StringBuffer(2 * n);
int k = m + n;
for (int l = m; l < k; l++) {
appendHexPair(bytes[l], stringbuffer);
}
return stringbuffer.toString();
}

private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
char c0 = hexDigits[(bt & 0xf0) >> 4];// 取字节中高 4 位的数字转换, >>>
// 为逻辑右移,将符号位一起右移,此处未发现两种符号有何不同
char c1 = hexDigits[bt & 0xf];// 取字节中低 4 位的数字转换
stringbuffer.append(c0);
stringbuffer.append(c1);
}

}

2.JWT令牌使用

导入pom坐标

1
2
3
4
5
6
<!--JWT起步依赖-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.3.0</version>
</dependency>

创建工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class JwtUtil {
//进行加密的key
private static final String KEY = "forexample";

//接收业务数据,生成token并返回,这里用的是userId和username
public static String genToken(Map<String, Object> claims) {
return JWT.create()
.withClaim("claims", claims)
.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
.sign(Algorithm.HMAC256(KEY));
}

//接收token,验证token,并返回业务数据
public static Map<String, Object> parseToken(String token) {
return JWT.require(Algorithm.HMAC256(KEY))
.build()
.verify(token)
.getClaim("claims")
.asMap();
}
}

在登陆时返回给前端,在进行其他接口调用的时候,将token传会后端,并且对请求进行拦截

在目录中创建拦截器文件interceptors并且定义拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//验证令牌
String token =request.getHeader("Authorization");
try {
//验证token
Map<String, Object> stringObjectMap = JwtUtil.parseToken(token);
ThreadLocalUtil.set(stringObjectMap);
//放行
return true;
} catch (Exception e) {
response.setStatus(401);
log.info("非法请求");
return false;
}
}
//在拦截器执行完后
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//清除thread里面的数据,防止内存泄漏
ThreadLocalUtil.remove();
}
}

在配置文件中,对该自定义拦截器进行注册,但是对登陆 和注册接口

1
2
3
4
5
6
7
8
9
10
11
@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
log.info("开始注册自定义拦截器");
registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login","/user/register");
}
}

3.后端工程中websocket的使用

导入webscoked的maven坐标

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

创建webscoked的服务文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.example.text;

import jakarta.websocket.OnClose;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

@Component
@ServerEndpoint("/ws/{sid}")
public class webSocked {
private static Map<String, Session> sessionMap =new HashMap();
/**
* 链接成功的回调方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid){
System.out.println("与客户端"+sid+"建立链接");
sessionMap.put(sid,session);
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, @PathParam("sid") String sid) {
System.out.println("收到来自客户端:" + sid + "的信息:" + message);
}
/**
* 连接关闭调用的方法
*
* @param sid
*/
@OnClose
public void onClose(@PathParam("sid") String sid) {
System.out.println("连接断开:" + sid);
sessionMap.remove(sid);
}
/**
* 群发
*
* @param message
*/
public void sendToAllClient(String message) {
Collection<Session> sessions =sessionMap.values();
for (Session session : sessions) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

}

将webscoked注入配置类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.example.text;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class webSockedConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}

创建测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.example.text;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Component
public class webSockedText {
@Autowired
private webSocked webSocked;
@Scheduled(cron = "0/5 * * * * ?")
public void sendMessage() {
webSocked.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));
}
}

定时任务在程序主入口出添加允许任务的注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.text;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
//springboot注解
@SpringBootApplication
//lombok注解
@Slf4j
//开启事务注解
@EnableScheduling
public class TextApplication {

public static void main(String[] args) {
SpringApplication.run(TextApplication.class, args);
log.info("服务启动");
}

}

4.双token

首先导入jwt的pom依赖

1
2
3
4
5
 <dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.3.0</version>
</dependency>

result结果类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.example.text;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class result<T> {
private Integer code;
private String message;
private T data;
public static <E> result<E> success(E data){
return new result<E>(0,"操作成功",data);
}
public static result success(){
return new result(0,"操作成功",null);
}
public static result error(String message) {
return new result(1, message, null);
}
}

编写一个jwt工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.example.text;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;

import java.util.Date;
import java.util.Map;

public class jwt {

private static final String KEY = "forexample";

//接收业务数据,生成token并返回
public static String genToken(Map<String, Object> claims, int minute) {
return JWT.create()
.withClaim("claims", claims)
.withExpiresAt(new Date(System.currentTimeMillis() +1000*minute))//1000 * 60 * 60 * 12
.sign(Algorithm.HMAC256(KEY));
}

//接收token,验证token,并返回业务数据
public static Map<String, Object> parseToken(String token) {
return JWT.require(Algorithm.HMAC256(KEY))
.build()
.verify(token)
.getClaim("claims")
.asMap();
}

}

定义一个拦截器,核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.example.text;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import com.example.text.jwt;

import java.awt.*;
import java.util.Map;

@Slf4j
@Component
public class login implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
//请求拦截器
//获得请求头中的token
String accessToken = request.getHeader("accessToken");
colorPrint.colorPrint("accessToken----" + accessToken, colorPrint.BLUE);
String refreshToken = request.getHeader("refreshToken");
colorPrint.colorPrint("refreshToken----" + refreshToken, colorPrint.BLUE);
try {
//解析accessToken
Map<String, Object> map = jwt.parseToken(accessToken);
colorPrint.colorPrint("请求token通过,允许访问", colorPrint.BLUE);
//成功
return true;
} catch (Exception e) {
//失败,开始解析refreshtoken,判断是否过期
try {
Map<String, Object> map = jwt.parseToken(refreshToken);
} catch (Exception exception) {
//refreshtoken失效
colorPrint.colorPrint("七天请求过期", colorPrint.RED);
response.setHeader("isOverdue","true");
response.setStatus(401);
return false;
}
//refreshtoken有效期没过
colorPrint.colorPrint("非法请求", colorPrint.RED);
response.setStatus(401);
return false;
}
}
}

为了打印彩色的语句,自己封装了一个彩色打印

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.example.text;

public class colorPrint {
public static final Integer RED=31;
public static final Integer BLACK=30;
public static final Integer GREEN=32;
public static final Integer ORIGIN=33;
public static final Integer BLUE=34;
public static final Integer PURPLE=35;
public static final Integer AQUA=36;
public static final Integer GRAY=37;
public static void colorPrint(String message, Integer color) {
System.out.println("\033["+color+";4m" + message + "\033[0m");
}
}

将自定义拦截器注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.example.text;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class config implements WebMvcConfigurer {
@Autowired
private login login;

@Override
public void addInterceptors(InterceptorRegistry registry) {
colorPrint.colorPrint(" 自定义拦截器开始生效",colorPrint.GREEN);
//除了获得token和refresh请求,其他的请求都要经过拦截器
registry.addInterceptor(login).excludePathPatterns("/get","/refresh");
}
}

定义请求层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.example.text;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@RestController
public class controller {
@GetMapping("/text")
public result textService() {
colorPrint.colorPrint("请求进入", colorPrint.ORIGIN);
return result.success();
}

@GetMapping("/get")
public result<Map> get() {
Map<String, Object> clains = new HashMap<>();
clains.put("id", 1);
clains.put("username", 666);
log.info("生成jwt令牌");
String accessToken = jwt.genToken(clains, 3);
String refreshToken = jwt.genToken(clains, 60);
colorPrint.colorPrint(accessToken, colorPrint.RED);
colorPrint.colorPrint(refreshToken, colorPrint.BLUE);
Map map = new HashMap<>();
map.put("accessToken", accessToken);
map.put("refreshToken", refreshToken);

return result.success(map);
}

@GetMapping("/refresh")
public result<Map> refresh(@RequestHeader(name = "refreshToken") String refreshToken) {
Map<String, Object> map = jwt.parseToken(refreshToken);
String newAccessToken = jwt.genToken(map, 3);
String newRefreshToken = jwt.genToken(map, 60);
Map hashMap = new HashMap<>();
colorPrint.colorPrint(newAccessToken, colorPrint.PURPLE);
colorPrint.colorPrint(newRefreshToken, colorPrint.PURPLE);
hashMap.put("accessToken", newAccessToken);
hashMap.put("refreshToken", newRefreshToken);
return result.success(hashMap);
}


}

5.图形验证码使用

验证码生成工具

本项目使用开源的验证码生成工具EasyCaptcha,其支持多种类型的验证码,例如gif、中文、算术等,并且简单易用,具体内容可参考其官方文档

common模块的pom.xml文件中增加如下内容

1
2
3
4
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
</dependency>

在service中使用

1
2
3
4
5
SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 4);
specCaptcha.setCharType(Captcha.TYPE_DEFAULT);
String code = specCaptcha.text().toLowerCase();
System.out.println(code);
String image = specCaptcha.toBase64();

启动项目当有请求进入的时候,会生成验证码的code,code是验证吗的值,image由base64编码,到前端进行解码,此处并没有使用redis,正常情况下需要生成一个uuid作为redis存储的key,而code作为redis的value。返回给前端的是验证码图片imag和redis的key,当前端有请求进来时,携带用户名和密码,图片验证码key和code,进入后端查询redis,进行正常的登陆