http通信加解密

soul2/encrypt
soul2 1 year ago
parent 5e5fb9a07d
commit e044f02d32
  1. 2
      src/main/java/cn/soul2/jyjc/admin/JavaJyjcAdminApplication.java
  2. 18
      src/main/java/cn/soul2/jyjc/admin/annotation/SkinEncrypt.java
  3. 41
      src/main/java/cn/soul2/jyjc/admin/config/FilterConfig.java
  4. 6
      src/main/java/cn/soul2/jyjc/admin/config/WebMvcConfig.java
  5. 40
      src/main/java/cn/soul2/jyjc/admin/filter/ReplaceStreamFilter.java
  6. 134
      src/main/java/cn/soul2/jyjc/admin/filter/ShaoduoRequestWrapper.java
  7. 128
      src/main/java/cn/soul2/jyjc/admin/interceptor/FinallyInterceptor.java
  8. 63
      src/main/java/cn/soul2/jyjc/admin/interceptor/TokenInterceptor.java
  9. 20
      src/main/java/cn/soul2/jyjc/admin/utils/AesUtils.java
  10. 132
      src/main/java/cn/soul2/jyjc/admin/utils/EncryptUtils.java
  11. 2
      src/main/resources/application.yml

@ -3,6 +3,7 @@ package cn.soul2.jyjc.admin;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
/**
* @author Soul2
@ -10,6 +11,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
*/
@SpringBootApplication
@MapperScan("cn.soul2.jyjc.admin.mapper")
@ComponentScan("cn.soul2.jyjc.admin.config")
public class JavaJyjcAdminApplication {
public static void main(String[] args) {

@ -0,0 +1,18 @@
package cn.soul2.jyjc.admin.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 跳过加解密
*
* @author Soul2
* @date 2024-04-08
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SkinEncrypt {
}

@ -0,0 +1,41 @@
package cn.soul2.jyjc.admin.config;
import cn.soul2.jyjc.admin.filter.ReplaceStreamFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
/**
* @author Soul2
* @description 过滤器配置类
* @date 2024-04-08
* <p>照抄自 <a href="https://blog.csdn.net/shaoduo/article/details/122322578">Shao duo</a></p>
*/
@Configuration
public class FilterConfig {
/**
* 注册过滤器
*
* @return FilterRegistrationBean
*/
@Bean
public FilterRegistrationBean someFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(replaceStreamFilter());
registration.addUrlPatterns("/*");
registration.setName("streamFilter");
return registration;
}
/**
* 实例化StreamFilter
*
* @return Filter
*/
@Bean(name = "replaceStreamFilter")
public Filter replaceStreamFilter() {
return new ReplaceStreamFilter();
}
}

@ -1,12 +1,12 @@
package cn.soul2.jyjc.admin.config;
import cn.soul2.jyjc.admin.interceptor.TokenInterceptor;
import cn.soul2.jyjc.admin.interceptor.FinallyInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 注册请求拦截器
* 注册拦截器
*
* @author Soul2
* @date 2024-04-02
@ -17,7 +17,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokenInterceptor())
registry.addInterceptor(new FinallyInterceptor())
// 拦截所有路径
.addPathPatterns("/**");
}

@ -0,0 +1,40 @@
package cn.soul2.jyjc.admin.filter;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author shaoduo
* @description 替换HttpServletRequest
* @since 1.0
**/
@Slf4j
public class ReplaceStreamFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("StreamFilter初始化...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//如果是文件上传则会报错可以判断是否是文件上传不读取流即可
if (ServletFileUpload.isMultipartContent((HttpServletRequest) request)) {
chain.doFilter(request, response);
return;
} else {
ServletRequest requestWrapper = new ShaoduoRequestWrapper((HttpServletRequest) request);
chain.doFilter(requestWrapper, response);
return;
}
}
@Override
public void destroy() {
log.info("StreamFilter销毁...");
}
}

@ -0,0 +1,134 @@
package cn.soul2.jyjc.admin.filter;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* @author shaoduo
* @description 包装HttpServletRequest目的是让其输入流可重复读
* @since 1.0
**/
@Slf4j
public class ShaoduoRequestWrapper extends HttpServletRequestWrapper {
/**
* 存储body数据的容器
*/
private byte[] body;
public ShaoduoRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
// 将body数据存储起来
String bodyStr = getBodyString(request);
body = bodyStr.getBytes(Charset.defaultCharset());
}
/**
* 获取请求Body
*
* @param request request
* @return String
*/
public String getBodyString(final ServletRequest request) {
try {
return inputStream2String(request.getInputStream());
} catch (IOException e) {
log.error("", e);
throw new RuntimeException(e);
}
}
/**
* 获取请求Body
*
* @return String
*/
public String getBodyString() {
InputStream inputStream = new ByteArrayInputStream(body);
return inputStream2String(inputStream);
}
/**
* 修改body 将json 重新设置成body
*
* @param val
*/
public void setBody(String val) {
body = val.getBytes(StandardCharsets.UTF_8);
}
/**
* 将inputStream里的数据读取出来并转换成字符串
*
* @param inputStream inputStream
* @return String
*/
private String inputStream2String(InputStream inputStream) {
StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
log.error("", e);
throw new RuntimeException(e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
log.error("", e);
}
}
}
return sb.toString();
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return inputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}

@ -0,0 +1,128 @@
package cn.soul2.jyjc.admin.interceptor;
import cn.soul2.jyjc.admin.annotation.SkinEncrypt;
import cn.soul2.jyjc.admin.annotation.SkinLogin;
import cn.soul2.jyjc.admin.config.UserLoginStatusBean;
import cn.soul2.jyjc.admin.filter.ShaoduoRequestWrapper;
import cn.soul2.jyjc.admin.utils.EncryptUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.MediaType;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
/**
* Token拦截器
*
* @author Soul2
* @date 2024-04-02 15:31
*/
@Slf4j
public class FinallyInterceptor implements HandlerInterceptor {
@Autowired
@Lazy
private ApplicationContext context;
@Resource
@Lazy
private UserLoginStatusBean userLoginStatusBean;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean pass = false;
// 允许OPTIONS请求通过
if ("OPTIONS".equals(request.getMethod()) && request.getHeader("Origin") != null) {
return true;
}
// 如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
String httpMethod = request.getMethod();
SkinEncrypt skinEncrypt = handlerMethod.getMethodAnnotation(SkinEncrypt.class);
// 检查方法上是否存在SkinLogin注解
boolean hasSkinLogin = handlerMethod.getMethodAnnotation(SkinLogin.class) != null;
// 从请求头中获取 token
String token = request.getHeader("jyjc-Token");
try {
/*
加解密
*/
// 不拦截get请求
if ("GET".equals(httpMethod)) {
pass = true;
// 如果是post请求且是json
} else if ("POST".equals(httpMethod)) {
// 如果类型为空就放行
if (request.getContentType() == null) {
pass = true;
}
// 如果类型不是json 就放行
if (!(request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE))) {
pass = true;
}
}
// 跳过使用SkinEncrypt注解的情况
if (skinEncrypt != null) {
pass = true;
}
ShaoduoRequestWrapper shaoduoRequestWrapper;
if (request instanceof ShaoduoRequestWrapper) {
shaoduoRequestWrapper = (ShaoduoRequestWrapper) request;
} else {
shaoduoRequestWrapper = new ShaoduoRequestWrapper(request);
request = shaoduoRequestWrapper;
}
String jsonParamBody = shaoduoRequestWrapper.getBodyString();
JSONObject obj = JSON.parseObject(EncryptUtils.decrypt(jsonParamBody));
String afterBody = JSONObject.toJSONString(obj);
System.out.println("加密前 " + jsonParamBody);
System.out.println("解密后 " + afterBody);
shaoduoRequestWrapper.setBody(afterBody);
String temp = new ShaoduoRequestWrapper(shaoduoRequestWrapper).getBodyString();
System.out.println("过滤器中缓存 " + temp);
/*
token验证
*/
if (hasSkinLogin) {
// 如果存在,绕过拦截器
pass = true;
} else {
// 验证token
if (userLoginStatusBean == null) {
userLoginStatusBean = context.getBean(UserLoginStatusBean.class);
}
// 检查 token 是否存在并且有效
if (token == null) {
// 没有Token,拒绝请求
response.setStatus(40401);
pass = false;
} else if (userLoginStatusBean != null && !userLoginStatusBean.containsToken(token)) {
// Token 无效,拒绝请求,可以返回特定的响应状态码,例如 401 Unauthorized
response.setStatus(40401);
pass = false;
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return pass;
}
}

@ -1,63 +0,0 @@
package cn.soul2.jyjc.admin.interceptor;
import cn.soul2.jyjc.admin.annotation.SkinLogin;
import cn.soul2.jyjc.admin.config.UserLoginStatusBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author Soul2
* @date 2024-04-02 15:31
*/
public class TokenInterceptor implements HandlerInterceptor {
@Autowired
private UserLoginStatusBean userLoginStatusBean;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 允许OPTIONS请求通过
if (isCorsRequest(request)) {
return true;
}
// 跳过登录
// 如果处理器是一个方法处理器
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 检查方法上是否存在SkinLogin注解
if (handlerMethod.getMethod().isAnnotationPresent(SkinLogin.class)) {
// 如果存在,绕过拦截器
return true;
}
}
// 验证token
// 从请求头中获取 token
String token = request.getHeader("jyjc-Token");
// 检查 token 是否存在并且有效,这里可以根据实际情况自行实现验证逻辑
if (token != null && isValidToken(token)) {
// Token 有效,允许请求通过
return true;
} else {
// Token 无效,拒绝请求,可以返回特定的响应状态码,例如 401 Unauthorized
response.setStatus(40401);
return false;
}
}
private boolean isValidToken(String token) {
// 实现 token 验证逻辑,例如验证 token 的签名或者在数据库中验证 token 的有效性
return userLoginStatusBean.containsToken(token);
// 返回 true 表示 token 有效,返回 false 表示 token 无效
}
private boolean isCorsRequest(HttpServletRequest request) {
// 检查请求头中是否包含 Origin 字段,如果存在,则认为是跨域请求
return request.getHeader("Origin") != null;
}
}

@ -1,7 +1,5 @@
package cn.soul2.jyjc.admin.utils;
import org.springframework.beans.factory.annotation.Value;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
@ -13,31 +11,30 @@ import java.util.Base64;
public class AesUtils {
@Value("${encrypt.keys.aes}")
private String aesKey;
private static final String AES_KEY = "98478f8a45887eda446501bcb44010520c3f53c7b45ff36215e86ce4f049b0f2";
private static final String AES_ALGORITHM = "AES";
private SecretKeySpec generateKey(String key) {
private static SecretKeySpec generateKey(String key) {
return new SecretKeySpec(key.getBytes(), AES_ALGORITHM);
}
public String encrypt(String source) throws Exception {
return encrypt(source, aesKey);
public static String encrypt(String source) throws Exception {
return encrypt(source, AES_KEY);
}
public String decrypt(String encrypted) throws Exception {
return decrypt(encrypted, aesKey);
public static String decrypt(String encrypted) throws Exception {
return decrypt(encrypted, AES_KEY);
}
private String encrypt(String source, String key) throws Exception {
public static String encrypt(String source, String key) throws Exception {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, generateKey(key));
byte[] encryptedBytes = cipher.doFinal(source.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
private String decrypt(String encrypted, String key) throws Exception {
public static String decrypt(String encrypted, String key) throws Exception {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, generateKey(key));
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encrypted));
@ -45,7 +42,6 @@ public class AesUtils {
}
public static void main(String[] args) {
}

@ -0,0 +1,132 @@
package cn.soul2.jyjc.admin.utils;
import org.apache.tomcat.util.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
/**
* @author Soul2
* @date 2024-04-08 15:00
* <p>照抄自 <a href="https://blog.csdn.net/shaoduo/article/details/122322578">Shao duo</a></p>
*/
public class EncryptUtils {
private static final String KEY = "620b8d3c3be1e725";
//参数分别代表 算法名称/加密模式/数据填充方式
private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";
/**
* 加密
*
* @param content 加密的字符串
* @param encryptKey key值
* @return {@link String}
*/
public static String encrypt(String content, String encryptKey) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));
byte[] b = cipher.doFinal(content.getBytes("utf-8"));
// 采用base64算法进行转码,避免出现中文乱码
return Base64.encodeBase64String(b);
}
/**
* 解密
*
* @param encryptStr 解密的字符串
* @param decryptKey 解密的key值
* @return {@link String}
*/
public static String decrypt(String encryptStr, String decryptKey) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes("utf-8"), "AES"));
// 采用base64算法进行转码,避免出现中文乱码
//byte[] b = hex2Bytes(encryptStr) ;
byte[] encryptBytes = Base64.decodeBase64(encryptStr);
byte[] decryptBytes = cipher.doFinal(encryptBytes);
return new String(decryptBytes);
}
/**
* @param content 加密的字符串
* @return {@link String}
*/
public static String encrypt(String content) throws Exception {
return encrypt(content, KEY);
}
/**
* @param encryptStr 解密的字符串
* @return {@link String}
*/
public static String decrypt(String encryptStr) throws Exception {
return decrypt(encryptStr, KEY);
}
/* public static void main(String[] args) throws Exception {
Map map=new HashMap<String,String>();
map.put("key","value");
map.put("中文","汉字");
String content = JSONObject.toJSONString(map);
System.out.println("加密前:" + content);
String encrypt = encrypt(content, KEY);
System.out.println("加密后:" + encrypt);
String decrypt = decrypt(encrypt, KEY);
System.out.println("解密后:" + decrypt);
}*/
/**
* byte数组 转换成 16进制小写字符串
*/
public static String bytes2Hex(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
return null;
}
StringBuilder hex = new StringBuilder();
for (byte b : bytes) {
hex.append(HEXES[(b >> 4) & 0x0F]);
hex.append(HEXES[b & 0x0F]);
}
return hex.toString();
}
/**
* 16进制字符串 转换为对应的 byte数组
*/
public static byte[] hex2Bytes(String hex) {
if (hex == null || hex.length() == 0) {
return null;
}
char[] hexChars = hex.toCharArray();
// 如果 hex 中的字符不是偶数个, 则忽略最后一个
byte[] bytes = new byte[hexChars.length / 2];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) Integer.parseInt("" + hexChars[i * 2] + hexChars[i * 2 + 1], 16);
}
return bytes;
}
private static final char[] HEXES = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
};
}

@ -4,8 +4,6 @@ server:
#上下文
servlet.context-path: /thli/jyjc/api
encrypt.keys.aes: 98478f8a45887eda446501bcb44010520c3f53c7b45ff36215e86ce4f049b0f2
spring:
application.name: jyjc-admin
profiles.include: datasource,mybatis-plus,cors

Loading…
Cancel
Save