Token(JWT)
前期准备:
<!-- JWT依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
jdk9版本及以上可能会出现报错需要添加一下三个坐标:
1.这些依赖项将添加 javax.xml.bind.DatatypeConverter 类的实现,并解决在Java 9或更高版本中出现的 ClassNotFoundException 错误。
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.1</version>
</dependency>
如果需要使用JSONObject导入这个坐标
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
实例代码:
/**
* JwtUtils类,用于生成和解析JWT
*/
public class JwtUtils {
// JWT密钥
private static String jwt = "woshibaba";
// JWT过期时间(毫秒)
private static Long expire = 36000000L;
/**
* 生成JWT
* @param map 包含声明的键值对Map
* @return 生成的JWT字符串
*/
public static String generateJwt(Map<String,Object> map){
// 使用HS256签名算法和密钥jwt生成JWT
String compact = Jwts.builder()
.addClaims(map)
.signWith(SignatureAlgorithm.HS256,jwt)
.setExpiration(new Date(System.currentTimeMillis()+expire))
.compact();
return compact;
}
/**
* 解析JWT
* @param jwts 待解析的JWT字符串
* @return 解析出的声明对象
*/
public static Claims parsJwt(String jwts){
// 使用密钥jwt和HS256签名算法验证JWT签名,并解析出声明
Claims body = Jwts.parser()
.setSigningKey(jwt)
.parseClaimsJws(jwts)
.getBody();
return body;
}
}
过滤器(Filter)
前期准备:
基于SpringMVC实现的
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
如果需要使用JSONObject导入这个坐标
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
示例代码:
使用前需要在主启动类上加上@ServletComponentScan注解
@ServletComponentScan
/**
* LoginCheckFilter类,用于检查用户登录状态
*/
@WebFilter(urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {
/**
* 过滤器的doFilter方法,用于检查用户登录状态
* @param servletRequest 表示HTTP请求对象
* @param servletResponse 表示HTTP响应对象
* @param filterChain 表示过滤器链
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 将ServletRequest和ServletResponse对象转换为HttpServletRequest和HttpServletResponse对象
HttpServletRequest httpServletRequest= (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse= (HttpServletResponse) servletResponse;
// 打印日志,表示拦截到了请求
log.info("Filter:{}","拦截到了");
// 获取请求的URL
String url = httpServletRequest.getRequestURL().toString();
// 如果请求的URL包含"login",则放行
if(url.contains("login")){
filterChain.doFilter(servletRequest,servletResponse);
return;
}
// 否则,获取请求头中的token
String token = httpServletRequest.getHeader("token");
// 如果token为空,表示用户未登录
if(token==null){
log.error("LoginCheckFilter:{}","未登录");
// 将错误信息封装成Result对象,并转换成JSON格式字符串
Result<Object> log = Result.error("NOT_LOGIN");
String json = JSONObject.toJSONString(log);
// 配置响应格式和编码格式
httpServletResponse.setContentType("application/json;charset=utf-8");
// 向客户端发送JSON格式字符串
httpServletResponse.getWriter().write(json);
return;
}
// 否则,使用JWT工具类解析token
try {
JwtUtils.parsJwt(token);
} catch (Exception e) {
// 如果解析失败,表示token不合法
log.error("JwtUtils.parsJwt(token)解析失败{}",e.getLocalizedMessage());
// 将错误信息封装成Result对象,并转换成JSON格式字符串
Result<Object> error = Result.error(e.getLocalizedMessage());
String json = JSONObject.toJSONString(error);
// 配置响应格式和编码格式
httpServletResponse.setContentType("application/json;charset=utf-8");
// 向客户端发送JSON格式字符串
httpServletResponse.getWriter().write(json);
return;
}
// 如果token合法,放行请求
filterChain.doFilter(servletRequest,servletResponse);
}
}
拦截器(Interceptor)
前期准备
他是基于Spring实现的
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
如果需要使用JSONObject导入这个坐标
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
登录校验拦截器
@Component // 声明该类为Spring组件,可以被Spring自动扫描并注入依赖
@Slf4j // 使用Lombok注解,自动生成日志对象
public class LoginCheckInterceptor implements HandlerInterceptor { // 实现HandlerInterceptor接口,用于拦截HTTP请求并检查用户是否已通过身份验证
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在控制器处理请求之前拦截请求,并进行身份验证
String url = request.getRequestURL().toString(); // 获取请求的URL
if(url.contains("login")){ // 如果请求的URL包含"login",说明用户正在登录,直接放行
return true;
}
String token = request.getHeader("token"); // 获取请求头中的"token"字段,用于身份验证
log.info("Interceptor:{}","拦截到了"); // 记录日志,表示拦截到了请求
if(token==null){ // 如果请求头中没有"token"字段,说明用户未登录,返回错误信息并拦截请求
log.error("LoginCheckFilter:{}","未登录"); // 记录错误日志,表示用户未登录
Result<Object> log = Result.error("NOT_LOGIN"); // 创建一个Result对象,表示未登录错误
String json = JSONObject.toJSONString(log); // 将Result对象转换为JSON字符串
response.setContentType("application/json;charset=utf-8"); // 设置响应的Content-Type为JSON格式
response.getWriter().write(json); // 向客户端发送错误消息
return false; // 拦截请求
}
try {
JwtUtils.parsJwt(token); // 使用JwtUtils类的parsJwt方法解析令牌,如果解析失败会抛出异常
} catch (Exception e) {
log.error("JwtUtils.parsJwt(token)解析失败{}",e.getLocalizedMessage()); // 记录错误日志,表示令牌无效
Result<Object> log = Result.error(e.getLocalizedMessage()); // 创建一个Result对象,表示令牌无效错误
String json = JSONObject.toJSONString(log); // 将Result对象转换为JSON字符串
response.setContentType("application/json;charset=utf-8"); // 设置响应的Content-Type为JSON格式
response.getWriter().write(json); // 向客户端发送错误消息
return false; // 拦截请求
}
return true; // 身份验证通过,放行请求
}
}
注册配置拦截器
@Configuration // 声明该类为Spring配置类,用于配置应用程序上下文
public class LoginWebIntercepter implements WebMvcConfigurer { // 实现WebMvcConfigurer接口,用于配置Web应用程序
@Autowired // 自动注入LoginCheckInterceptor对象
LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器,用于拦截HTTP请求并进行身份验证
registry.addInterceptor(loginCheckInterceptor) // 添加LoginCheckInterceptor拦截器
.addPathPatterns("/**") // 对所有请求进行拦截
.excludePathPatterns("/login"); // 排除/login请求,因为该请求用于用户登录
}
}
全局异常处理器
前期准备:
创建handler包
他是基于Spring实现的
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
示例代码:
@RestControllerAdvice // 声明该类为全局异常处理类,用于统一处理应用程序中的异常
@Slf4j // 使用Lombok的@Slf4j注解,自动生成日志对象
public class GlobalExceptionProcessor extends RuntimeException{
// 继承RuntimeException类,表示该类本身也是一个异常类型
@ExceptionHandler(RuntimeException.class) // 声明该方法用于处理RuntimeException类型的异常
public Result runtimeException(RuntimeException e){
log.info("RuntimeException:{}",e.getLocalizedMessage()); // 记录异常信息到日志中
return Result.error(e.getLocalizedMessage()); // 返回一个Result对象,表示处理结果
}
}
对象存储(OSS)(创建获取:https://editor.csdn.net/md/?articleId=131270244)
前期准备:
- 在properties文件中配置阿里OSS
#- endpoint //阿里云OSS域名
#- accessKeyID //用户身份ID
#- accessKeySecret //用户密钥
#- bucketName //存储空间的名字
aliyun.oss.endpoint=https://oss-cn-****.aliyuncs.com
aliyun.oss.accessKeyId=LTAI4*******6nEuW
aliyun.oss.accessKeySecret=yBshY******KpyqSL
aliyun.oss.bucketName=web-tlias
- 配置yml
# 注意! 冒号(:)后有一个空格不要忘记
aliyun:
oss:
endpoint: 阿里云OSS域名
accessKeyId: 用户身份ID
accessKeySecret: 用户密钥
bucketName: 存储空间的名字
两种:properties和yml(根据项目需求使用)!!!
注解使用:@ConfigurationProperties("aliyun.oss")
:可以代替@Value 跟简单
- @Value注解只能一个一个的进行外部属性的注入。
- @ConfigurationProperties可以批量的将外部的属性配置注入到bean对象的属性中。
代码示例:
@Component
//@ConfigurationProperties("aliyun.oss")两种可以任意选择(二选一)
public class AliOssUtils {
@Value("${aliyun.oss.endpoint}")
private String endpoint; //OSS的访问域名
@Value("${aliyun.oss.accessKeyId}")
private String accessKeyId; //OSS的Access Key ID
@Value("${aliyun.oss.accessKeySecret}")
private String accessKeySecret; //OSS的Access Key Secret
@Value("${aliyun.oss.bucketName}")
private String bucketName; //OSS的Bucket名称
/**
* 实现上传图片到OSS
*/
public String upload(MultipartFile multipartFile) throws IOException {
// 获取上传的文件的输入流
InputStream inputStream = multipartFile.getInputStream();
// 避免文件覆盖
String originalFilename = multipartFile.getOriginalFilename();
// 在前面拼接的这个tlias是你在仓库中定义的文件夹
String fileName = "tlias/"+ UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf(".")); //生成唯一的文件名
//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); //创建OSS客户端
ossClient.putObject(bucketName, fileName, inputStream); //上传文件
//文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName; //拼接文件访问路径
// 关闭ossClient
ossClient.shutdown(); //关闭OSS客户端
return url; //把上传到OSS的路径返回
}
}
本地存储
前期准备:
也可以模仿OSS中的代码
将路径提取到properties和yml文件中
代码示例:
* @param username 用户名
* @param age 年龄
* @param image 图片文件
* @return 返回上传后的图片URL
* @throws IOException IO异常
*/
@PostMapping("/upload")
public Result ossInsertIOURl(String username, Integer age, MultipartFile image) throws IOException {
//判断上传文件是否为空或者是否大于2M
if (image.isEmpty() || image.getSize() > 2097152) {
return Result.error("输入文件大于2M");
}
// 获取文件名后缀
String originalFilename = image.getOriginalFilename();
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
// 生成UUID作为文件名
UUID uuid = UUID.randomUUID();
// 拼接文件路径(将图片位置放在Nginx中html项目下:否则访问不到)
String filePath = "D:/Java/tlias-web-nginx/nginx-1.22.0-tlias/nginx-1.22.0-tlias/html/images/" + uuid + suffix;
// 创建文件对象
File destFile = new File(filePath);
//判断文件是否存在或者为空
if (destFile.exists() || destFile == null) {
return Result.error("文件路径为空");
}
// 将上传的文件保存到本地
image.transferTo(destFile);
// 拼接图片URL
String url = "./images/" + uuid + suffix;
// 返回上传后的图片URL
return Result.success(url);
}