首先来看一下项目结构
看看项目实际都集成了哪些模块
首先来看一下代码的目录结构
自定义异常
/**
* @description: 全局异常处理类
* @author: wubowen
* @date: 2021/5/25 0025 10:04
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理自定义异常
*/
@ExceptionHandler(value = BadRequestException.class)
public ResponseEntity<ApiError> badRequestException(BadRequestException e) {
return buildResponseEntity(ApiError.error(e.getStatus(), e.getMessage()));
}
/**
* 统一返回
*/
private ResponseEntity<ApiError> buildResponseEntity(ApiError apiError) {
return new ResponseEntity<>(apiError, HttpStatus.valueOf(apiError.getStatus()));
}
}
全局异常处理类
/**
* @description: 全局异常处理类
* @author: wubowen
* @date: 2021/5/25 0025 10:04
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理自定义异常
*/
@ExceptionHandler(value = BadRequestException.class)
public ResponseEntity<ApiError> badRequestException(BadRequestException e) {
return buildResponseEntity(ApiError.error(e.getStatus(), e.getMessage()));
}
/**
* 统一返回
*/
private ResponseEntity<ApiError> buildResponseEntity(ApiError apiError) {
return new ResponseEntity<>(apiError, HttpStatus.valueOf(apiError.getStatus()));
}
}
统一返回体实现
/**
* @description: 全局异常处理类
* @author: wubowen
* @date: 2021/5/25 0025 10:04
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理自定义异常
*/
@ExceptionHandler(value = BadRequestException.class)
public ResultV0<String> badRequestException(BadRequestException e) {
return new ResultV0<>(ResultCode.VALIDATE_FAILED, null);
}
}
/**
* @description: 响应码枚举类
* @author: wubowen
* @date: 2021/5/25 0025 14:46
*/
@Getter
public enum ResultCode {
SUCCESS(1000, "操作成功"),
VALIDATE_FAILED(1001, "参数校验失败");
private int code;
private String msg;
ResultCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
}
package com.stork.base;
import com.stork.utils.enums.ResultCode;
import lombok.Getter;
/**
* @description: 自定义统一的响应体
* @author: wubowen
* @date: 2021/5/26 0026 16:48
*/
@Getter
public class ResultV0<T> {
/**
* 状态码
*/
private int code;
/**
* 响应信息
*/
private String msg;
/**
* 响应的具体数据
*/
private T data;
public ResultV0(T data) {
this(ResultCode.SUCCESS, data);
}
public ResultV0(ResultCode resultCode, T data) {
this.code = resultCode.getCode();
this.msg = resultCode.getMsg();
this.data = data;
}
public ResultV0(ResultCode resultCode) {
this.code = resultCode.getCode();
this.msg = resultCode.getMsg();
this.data = null;
}
public ResultV0(ResultCode resultCode, String msg) {
this.code = resultCode.getCode();
this.msg = msg;
this.data = null;
}
}
package com.stork.global;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.stork.base.ResultV0;
import com.stork.exception.BadRequestException;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* @description: 全局处理响应数据
* 包装返回的实体类,统一返回体
* @author: wubowen
* @date: 2021/5/26 0026 17:03
*/
@RestControllerAdvice(basePackages = "com.stork.handler")
public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {
public static final String NEWSIMAGEUPLOADPATH = "/news/uploadImageForNews";
public static final String NEWSVIDEOUPLOADPATH = "/news/uploadVideoForNews";
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
/**
* 如果接口返回的类型本身就是ResultV0,那就没有必要进行额外操作,直接返回false
*/
return !methodParameter.getParameterType().equals(ResultV0.class);
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (serverHttpRequest.getURI().getPath().equals(NEWSIMAGEUPLOADPATH)
||serverHttpRequest.getURI().getPath().equals(NEWSVIDEOUPLOADPATH)) {
return o;
}
/**
* String类型不能直接包装,所以要进行些特别的处理
*/
if (methodParameter.getGenericParameterType().equals(String.class)) {
ObjectMapper objectMapper = new ObjectMapper();
try {
/**
* String类型不能直接包装,所以要进行些特别的处理
*/
return objectMapper.writeValueAsString(new ResultV0<>(o));
} catch (JsonProcessingException e) {
throw new BadRequestException("返回String类型错误");
}
}
/**
* 将原本的数据包装到ResultVO里面
*/
return new ResultV0<>(o);
}
}
@RestControllerAdvice注解必须加上扫描包的路径,否则会影响Swagger的使用
新建一个拦截器
package com.stork.global;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @description: 测试拦截器
* @author: wubowen
* @date: 2021/7/12 0012 15:31
*/
public class TestIntercepter implements HandlerInterceptor {
/** 在请求处理之前被调用,在Controller方法调用之前调用 **/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 此处为某些接口放行,不走以下逻辑
if (request.getRequestURI().contains("/.")) {
return true;
}
return false;
}
/** 在请求处理之后被调用,但是在视图渲染之前调用 **/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
}
配置新建的拦截器
package com.stork.config;
import com.stork.global.TestIntercepter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @description: 配置拦截器
* @author: wubowen
* @date: 2021/7/12 0012 15:47
*/
@Configuration
public class TestIntercepterConfiguration implements WebMvcConfigurer {
@Bean
public HandlerInterceptor getTestInterceptor() {
return new TestIntercepter();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getTestInterceptor()).addPathPatterns("/test");
}
}
看下目录结构
/**
* @description: 跨域配置
* @author: wubowen
* @date: 2021/5/26 0026 10:17
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 跨域配置
* addMapping:对哪种格式的请求路径进行处理
* allowedHeaders:允许的请求头
* allowedMethods:允许的请求方法
* maxAge:探测请求的有效期
* allowedOrigins:支持的域
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedMethods("*")
.maxAge(1800)
.allowedOrigins("*");
}
}
配置yml文件
# 阿里短信服务配置
sms:
sign-name : **********
template-code : **********
access-keyID : **********
access-keySecret : **********
导入短信依赖
<dependency>
<groupId>com.aliyungroupId>
<artifactId>aliyun-java-sdk-coreartifactId>
<version>4.5.3version>
dependency>
创建短信接口
package com.stork.service;
/**
* @description: 短信服务
* @author: wubowen
* @date: 2021/7/12 0012 17:51
*/
public interface SmsService {
/**
* 根据电话号码发送短信
* @param phone 电话号码
*/
void sendSmsCode(String phone);
/**
* 验证短信验证码正确性
* @param phone 电话号码
* @param code 验证码
* @return
*/
boolean validSmsCode(String phone, String code);
}
完成短信实际业务
@Service
public class SmsServiceImpl implements SmsService {
@Value("${sms.access-keyID}")
private String accessID;
@Value("${sms.access-keySecret}")
private String accessKey;
@Value("${sms.sign-name}")
private String signName;
@Value("${sms.template-code}")
private String templageCode;
@Autowired
private RedisUtils redisUtils;
// JSON处理工具
@Autowired
private ObjectMapper objectMapper;
@Override
public void sendSmsCode(String phone) {
// 是否频繁发送验证码
if (isSendOften(phone)) {
throw new BadRequestException("验证码请求过于频繁");
}
Sms sms = makeCode(phone);
ObjectNode smsJson = objectMapper.createObjectNode();
smsJson.put("code", sms.getSmsCode());
smsJson.put("product", "Stork");
try {
CommonResponse commonResponse = send(phone, signName, templageCode, smsJson);
if (commonResponse.getData() != null) {
redisCode(sms);
}
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
@Override
public boolean validSmsCode(String phone, String code) {
try {
Sms sms = objectMapper.readValue(String.valueOf(redisUtils.get(phone)), Sms.class);
if (sms == null) {
return false;
}
if (sms.getSmsCode().equals(code)) {
return true;
}
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return false;
}
/**
* 生成四位随机的验证码
* @param phone 电话号码
* @return
*/
private Sms makeCode(String phone) {
String smsCode = RandomUtils.getRandomString(4);
Sms sms = new Sms();
sms.setPhone(phone);
sms.setSmsCode(smsCode);
sms.setTime(System.currentTimeMillis());
return sms;
}
/**
* 验证这个电话短信发送是否频繁
* @param phone 电话号码
* @return
*/
private boolean isSendOften(String phone) {
if (redisUtils.get(phone) == null) {
return false;
}
// 反序列化
Sms sms = null;
try {
sms = objectMapper.readValue(String.valueOf(redisUtils.get(phone)), Sms.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
if (sms.getTime() + 60*1000 >= System.currentTimeMillis()) {
return true;
}
return false;
}
/**
* 将验证码缓存到redis,并且制定缓存时间为10分钟
* @param sms 短信实体
*/
private void redisCode(Sms sms) throws JsonProcessingException {
redisUtils.set(sms.getPhone(), objectMapper.writeValueAsString(sms), 10L, TimeUnit.MINUTES);
}
/**
* 发送短信
* @param phone
* @param signName
* @param templageCode
* @param params
* @return
*/
private CommonResponse send(String phone, String signName, String templageCode, ObjectNode params) throws JsonProcessingException {
DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessID, accessKey);
IAcsClient client = new DefaultAcsClient(profile);
CommonResponse response = null;
CommonRequest request = new CommonRequest();
request.setSysMethod(MethodType.POST);
request.setSysDomain("dysmsapi.aliyuncs.com");
request.setSysVersion("2017-05-25");
request.setSysAction("SendSms");
request.putQueryParameter("RegionId", "cn-chengdu");
request.putQueryParameter("PhoneNumbers", phone);
request.putQueryParameter("SignName", signName);
request.putQueryParameter("TemplateCode", templageCode);
request.putQueryParameter("TemplateParam", objectMapper.writeValueAsString(params));
try {
response = client.getCommonResponse(request);
} catch (ServerException e) {
e.printStackTrace();
} catch (ClientException e) {
e.printStackTrace();
}
return response;
}
}
阿里云基础配置
# 阿里云基础配置
aliyun:
accessKeyId : **********
accessKeySecret : **********
chengduEndpoint : https://oss-cn-chengdu.aliyuncs.com
导入 OSS 依赖
<dependency>
<groupId>com.aliyun.ossgroupId>
<artifactId>aliyun-sdk-ossartifactId>
<version>3.10.2version>
dependency>
创建 OSS 接口
package com.stork.service;
import com.aliyun.oss.model.OSSObject;
import org.springframework.web.multipart.MultipartFile;
/**
* @description: OSS服务
* @author: wubowen
* @date: 2021/7/14 0014 15:18
*/
public interface OSSService {
/**
* 文件上传接口
* @param endpoint
* @param bucketName
* @param objectName 上传文件到OSS时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。
* @param file 上传文件
*/
void uploadFile(String endpoint, String bucketName, String objectName, MultipartFile file);
/**
* 文件下载接口
* @param endpoint
* @param bucketName
* @param objectName 从OSS下载文件时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。
* @return
*/
OSSObject downloadFile(String endpoint, String bucketName, String objectName);
/**
* 文件删除接口
* @param endpoint
* @param bucketName
* @param objectName 填写文件完整路径。文件完整路径中不能包含Bucket名称。
*/
void deleteFile(String endpoint, String bucketName, String objectName);
}
完成 OSS 实现类
package com.stork.service.Impl;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.OSSObject;
import com.stork.service.OSSService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/**
* @description: some desc
* @author: wubowen
* @date: 2021/7/14 0014 15:45
*/
@Service
public class OSSServiceImpl implements OSSService {
@Value("${aliyun.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.accessKeySecret}")
private String accessKeySecret;
@Override
public void uploadFile(String endpoint, String bucketName, String objectName, MultipartFile file) {
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 上传文件到指定的存储空间(bucketName)并将其保存为指定的文件名称(objectName)。
try {
ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(file.getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
// 关闭OSSClient。
ossClient.shutdown();
}
@Override
public OSSObject downloadFile(String endpoint, String bucketName, String objectName) {
return null;
}
@Override
public void deleteFile(String endpoint, String bucketName, String objectName) {
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 删除文件或目录。如果要删除目录,目录必须为空。
ossClient.deleteObject(bucketName, objectName);
// 关闭OSSClient。
ossClient.shutdown();
}
}
引入依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
扫描指定包下所有Mapper
注意:如果mapper配置文件没有放在resources文件夹下需要重新在pom.xml中指定资源文件夹路径
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
includes>
resource>
<resource>
<directory>src/main/resourcesdirectory>
resource>
resources>
build>
引入依赖
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
配置 yml 文件
spring:
datasource:
url: jdbc:mysql://主机名:3306/yunying?useUnicode=true&serverTimezone=GMT%2B8&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true
username: 账号
password: 密码
driver-class-name: com.mysql.cj.jdbc.Driver
引入 Redis 依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
配置 Redis
redis:
#主机IP地址
host : localhost
#Redis服务器连接端口
port : 6379
#Redis服务器连接密码(默认为空)
#password :
#连接池最大连接数(使用负值表示没有限制)
max-active : 8
#连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait : -1
#连接池中的最大空闲连接
max-idle : 8
#连接池中的最小空闲连接
min-idle : 0
#连接超时时间(毫秒)
timeout : 30000
编写 Redis 工具类
/**
* @description: Redis工具类
* @author: wubowen
* @date: 2021/5/27 0027 14:39
*/
@Component
public class RedisUtils {
private static final Logger log = LoggerFactory.getLogger(RedisUtils.class);
private RedisTemplate<Object, Object> redisTemplate;
// @Value("${jwt.online-key}")
// private String onlineKey;
public RedisUtils(RedisTemplate<Object, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
return true;
}
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @param timeUnit 单位
*/
public boolean expire(String key, long time, TimeUnit timeUnit) {
try {
if (time > 0) {
redisTemplate.expire(key, time, timeUnit);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
return true;
}
/**
* 根据 key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(Object key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 查找匹配key
*
* @param pattern key
* @return /
*/
public List<String> scan(String pattern) {
ScanOptions options = ScanOptions.scanOptions().match(pattern).build();
RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
RedisConnection rc = Objects.requireNonNull(factory).getConnection();
Cursor<byte[]> cursor = rc.scan(options);
List<String> result = new ArrayList<>();
while (cursor.hasNext()) {
result.add(new String(cursor.next()));
}
try {
RedisConnectionUtils.releaseConnection(rc, factory);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return result;
}
/**
* 分页查询 key
*
* @param patternKey key
* @param page 页码
* @param size 每页数目
* @return /
*/
public List<String> findKeysForPage(String patternKey, int page, int size) {
ScanOptions options = ScanOptions.scanOptions().match(patternKey).build();
RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
RedisConnection rc = Objects.requireNonNull(factory).getConnection();
Cursor<byte[]> cursor = rc.scan(options);
List<String> result = new ArrayList<>(size);
int tmpIndex = 0;
int fromIndex = page * size;
int toIndex = page * size + size;
while (cursor.hasNext()) {
if (tmpIndex >= fromIndex && tmpIndex < toIndex) {
result.add(new String(cursor.next()));
tmpIndex++;
continue;
}
// 获取到满足条件的数据后,就可以退出了
if (tmpIndex >= toIndex) {
break;
}
tmpIndex++;
cursor.next();
}
try {
RedisConnectionUtils.releaseConnection(rc, factory);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return result;
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
public void del(String... keys) {
if (keys != null && keys.length > 0) {
if (keys.length == 1) {
boolean result = redisTemplate.delete(keys[0]);
log.debug("--------------------------------------------");
log.debug(new StringBuilder("删除缓存:").append(keys[0]).append(",结果:").append(result).toString());
log.debug("--------------------------------------------");
} else {
Set<Object> keySet = new HashSet<>();
for (String key : keys) {
keySet.addAll(redisTemplate.keys(key));
}
long count = redisTemplate.delete(keySet);
log.debug("--------------------------------------------");
log.debug("成功删除缓存:" + keySet.toString());
log.debug("缓存删除数量:" + count + "个");
log.debug("--------------------------------------------");
}
}
}
// ============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 批量获取
*
* @param keys
* @return
*/
public List<Object> multiGet(List<String> keys) {
Object obj = redisTemplate.opsForValue().multiGet(Collections.singleton(keys));
return null;
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间
* @param timeUnit 类型
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time, TimeUnit timeUnit) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, timeUnit);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
// ================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time);
}
return count;
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return /
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
return redisTemplate.opsForList().remove(key, count, value);
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
}
}
/**
* @param prefix 前缀
* @param ids id
*/
public void delByKeys(String prefix, Set<Long> ids) {
Set<Object> keys = new HashSet<>();
for (Long id : ids) {
keys.addAll(redisTemplate.keys(new StringBuffer(prefix).append(id).toString()));
}
long count = redisTemplate.delete(keys);
// 此处提示可自行删除
log.debug("--------------------------------------------");
log.debug("成功删除缓存:" + keys.toString());
log.debug("缓存删除数量:" + count + "个");
log.debug("--------------------------------------------");
}
}
测试
@ApiOperation("创建临时用户(用于测试Redis)")
@PostMapping("/addTempUser")
public String addTempUser(@RequestBody User user) {
redisUtils.set(user.getUsername(), user.getPassword());
String password = (String) redisUtils.get(user.getUsername());
return "您设置的密码为:" + password;
}
依赖导入
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>2.9.2version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>2.9.2version>
dependency>
配置
package com.stork.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.documentation.service.Parameter;
import java.util.ArrayList;
import java.util.List;
/**
* @description: 接口管理工具Swagger的配置类
* @author: wubowen
* @date: 2021/5/25 0025 17:13
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api(){
// 如果接口需要token验证,那么在header中新增一个参数
Parameter tokenPara = new ParameterBuilder()
.name("token")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build();
List<Parameter> paras = new ArrayList<>();
paras.add(tokenPara);
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.globalOperationParameters(paras);
}
}
引入 JWT 依赖
<dependency>
<groupId>com.auth0groupId>
<artifactId>java-jwtartifactId>
<version>3.8.2version>
dependency>
新建 JWT 工具类
package com.stork.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.stork.entities.StorkUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @description: JWT工具类,生成JWT和认证
* @author: wubowen
* @date: 2021/11/13 0013 9:48
*/
public class JwtUtil {
private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
/**
* 秘钥
*/
private static final String SECRET = "yunying_secret";
/**
* 过期时间
*/
private static final long EXPIRATION = 2592000L;
/**
* 生成用户token,设置超时时间
* @param storkUser
* @return
*/
public static String createToken(StorkUser storkUser) {
// 过期时间
Date expireDate = new Date(System.currentTimeMillis() + EXPIRATION * 1000);
// 设置JWT规则
Map<String, Object> rule = new HashMap<>();
rule.put("alg", "HS256");
rule.put("typ", "JWT");
// 创建token
String token = JWT.create()
.withHeader(rule)
.withClaim("username", storkUser.getUsername())
.withExpiresAt(expireDate)
.withIssuedAt(new Date())
.sign(Algorithm.HMAC256(SECRET));
return token;
}
/**
* 校验token并解析token
* @param token 需要验证的token
* @return
*/
public static Map<String, Claim> verifyToken(String token) {
DecodedJWT jwt = null;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
jwt = verifier.verify(token);
} catch (Exception e) {
logger.error(e.getMessage());
logger.error("token解码异常");
return null;
}
return jwt.getClaims();
}
}
拦截器中加入 token 验证
package com.stork.global;
import com.auth0.jwt.interfaces.Claim;
import com.stork.entities.StorkUser;
import com.stork.exception.BadRequestException;
import com.stork.utils.JwtUtil;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.Map;
/**
* @description: 接口请求拦截器
* @author: wubowen
* @date: 2021/7/12 0012 15:31
*/
public class RequestIntercepter implements HandlerInterceptor {
/** 在请求处理之前被调用,在Controller方法调用之前调用 **/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 此处为某些接口放行,不走以下逻辑
//if (request.getRequestURI().contains("/.")) {
// return true;
//}
// 获取header里的token
final String token = request.getHeader("token");
// 验证token的有效性
if (token == null) {
throw new BadRequestException("请求失败,请重新登录");
}
// 解析token
Map<String, Claim> userData = JwtUtil.verifyToken(token);
// 检查token是否过期
Date expireDate = userData.get("exp").asDate();
boolean isExpire = expireDate.after(new Date());
if (!isExpire) {
throw new BadRequestException("请求过期,请重新登录");
}
// 更新token
StorkUser storkUser = new StorkUser();
storkUser.setUsername(userData.get("username").asString());
String newToken = JwtUtil.createToken(storkUser);
response.setHeader("token", newToken);
return true;
}
/** 在请求处理之后被调用,但是在视图渲染之前调用 **/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
}
引入依赖
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
生成证书
输入命令行
keytool -genkey -alias yunyinghttps -keyalg RSA -keysize 2048 -keystore sang.p12 -validity 365
配置 yml 文件
server:
port: 8080
# https配置
ssl:
key-store: sang.p12
key-alias: yunyinghttps
key-store-password: ******