文章目录
前言
记录了交友app的一些东西
一、小细节工具使用
1.Mybatis-plus分页工具
配置分页插件,使用selectPage和new Page<>(page,pageSize);
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
-----------------------------------------------------------------------------------
//具体写法
@Autowired
private BlackListMapper blackListMapper;
public IPage<BlackList> queryBlacklist(Long userId,int page,int pageSize){
Page<BlackList> blackListPage = new Page<>(page,pageSize);
return blackListMapper.selectPage(blackListPage, new LambdaQueryWrapper<BlackList>().eq(BlackList::getUserId, userId).orderByDesc(BasePojo::getCreated));
}
2.判断redis中key是否存在,查看有多少key,eq判断执行
//key是否存在
redisTemplate.opsForHash().hasKey(redisKey, hashKey);
//此key存在多少数量
Long size = redisTemplate.opsForHash().size(RedisKey.getUserLikeKey(userId));
//使用eq还可以用判断语句来决定此eq是否执行
List<UserInfo> userInfoList = userInfoService.queryUserInfoList(
new LambdaQueryWrapper<UserInfo>()
.in(UserInfo::getUserId, userIdList)
.eq(StrUtil.equalsIgnoreCase(gender, "man"), UserInfo::getSex, SexEnum.MAN)
.eq(StrUtil.equalsIgnoreCase(gender, "woman"), UserInfo::getSex, SexEnum.WOMAN)
);
3.alibaba的fastJSON
//将json字符串解析为pojo对象
Goods goods = JSON.parseObject(sourceAsString, Goods.class);
4.stream使用map将id作为key
//stream流使用,类似数据库索引,将id当作key,快速查询
Map<Long, UserInfo> userInfoMap =
userInfoList.stream().collect(
Collectors.toMap(UserInfo::getUserId, Function.identity())
);
5.jackson 、 ObjectMapper
https://blog.csdn.net/qidasheng2012/article/details/105771052
@JsonProperty("userName")//使用注解字段属性,替代原字段属性
private String name;
@JsonIgnore//序列化时忽略该字段
private Long id;
@JsonIgnoreProperties({"id","userName","birthday"})//使用在类上,同时忽略多个
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss:SSS", timezone = "GMT+8")//日期格式化
private Date birthday;
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
mapper设置
private static final ObjectMapper mapper;
public static ObjectMapper getObjectMapper(){
return this.mapper;
}
static{
//创建ObjectMapper对象
mapper = new ObjectMapper()
//configure方法 配置一些需要的参数
// 转换为格式化的json 显示出来的格式美化
mapper.enable(SerializationFeature.INDENT_OUTPUT);
//序列化的时候序列对象的那些属性
//JsonInclude.Include.NON_DEFAULT 属性为默认值不序列化
//JsonInclude.Include.ALWAYS 所有属性
//JsonInclude.Include.NON_EMPTY 属性为 空(“”) 或者为 NULL 都不序列化
//JsonInclude.Include.NON_NULL 属性为NULL 不序列化
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
//反序列化时,遇到未知属性会不会报错
//true - 遇到没有的属性就报错 false - 没有的属性不会管,不会报错
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//如果是空对象的时候,不抛异常
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
// 忽略 transient 修饰的属性
mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);
//修改序列化后日期格式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//处理不同的时区偏移格式
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.registerModule(new JavaTimeModule());
}
mapper使用
//反序列化,通过json数据或者url,另外还有许多别的方式
Car car = objectMapper.readValue(carJson, Car.class);
Car car = objectMapper.readValue(url, Car.class);
//读取map对象
String jsonObject = "{\"brand\":\"ford\", \"doors\":5}";
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> jsonMap = objectMapper.readValue(
jsonObject, new TypeReference<Map<String,Object>>(){});
//序列化
String json = objectMapper.writeValueAsString(car)
6.Lombok
@Data @Builder @NoArgsConstructor @AllArgsConstructor
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>1.18.4</version>
</dependency>
二、糊涂工具使用
<!--糊涂工具-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.2</version>
</dependency>
//获取id字段集合
List<Object> publishIdList = CollUtil.getFieldValues(albumList, "publishId");
//数据转换
Long date = Convert.toLong(redisTemplate.opsForHash().get(RedisKey.getVisitorUserKey(), String.valueOf(userId)));
//时间戳转换
Long minDate = DateUtil.parseDateTime(today + " 00:00:00").getTime();
//对象转json字符串
JSONUtil.toJsonStr(Collections.singletonList(huanXinUser));
//vo对象别名
@Alias("xxx")
三、前端数据接收
@RestController
@RequestMapping("user")
@Slf4j
public class UserInfoController {
@Autowired
private UserInfoService userInfoService;
@PostMapping("loginReginfo")
public ResponseEntity<ErrorResult> loginReginfo(
@RequestBody Map<String,String> param, //接收请求体
@RequestHeader("Authorization") String token,//接收头信息
@RequestParam("file") MultipartFile multipartFile){//接收文件
}
}
四、vo对象写法
@Alias("xxx")属于糊涂工具
package com.tanhua.server.vo;
import cn.hutool.core.annotation.Alias;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfoVo {
@Alias("userId")
private Long id; //用户id
@Alias("logo")
private String avatar; //头像
@Alias("nickName")
private String nickname; //昵称
private String birthday; //生日 2019-09-11
private String age; //年龄
private String gender; //性别 man woman
private String city; //城市
@Alias("edu")
private String education; //学历
private String income; //月收入
@Alias("industry")
private String profession; //行业
private Integer marriage; //婚姻状态(0未婚,1已婚)
}
第五章、创建时间和更新时间的自动赋值
package com.tanhua.sso.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {//对标记了@TableField(fill = FieldFill.INSERT)的字段进行监控
Object created = getFieldValByName("created", metaObject);//获取元对象,判定是否创建了这个字段
if (null == created){//如果没有创建,就手动创建这个字段,赋值当前时间
setFieldValByName("created",new Date(),metaObject);
}
Object update = getFieldValByName("update", metaObject);//同上
if (null == update){
setFieldValByName("update",new Date(),metaObject);
}
}
@Override
public void updateFill(MetaObject metaObject) {//对标记了@TableField(fill = FieldFill.UPDATE)的字段进行监控
setFieldValByName("update",new Date(),metaObject);
}
}
pojo类继承这个pojo就可以实现自动赋值
package com.tanhua.sso.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.util.Date;
@Data
public abstract class BasePojo {
@TableField(fill = FieldFill.INSERT) //MybatisPlus自动填充
private Date created;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updated;
}
第六章、redis自增长完成id自增长效果
@Service
public class IdService {
@Autowired
private RedisTemplate<String,String> redisTemplate;
/**
* 使用redis的自增长特性,实现全局id自增长
*/
public Long createId(IdType idType){
String key = "TANHUA_ID_"+idType.toString();
return redisTemplate.opsForValue().increment(key);
}
}
第七章、异步实现
需要在启动类添加@EnableAsync注解,在服务类开启线程,返回值使用CompletableFuture<String>,
一般返回return CompletableFuture.completedFuture("ok");
return CompletableFuture.completedFuture("error");
@Service
@Slf4j
public class TimeLineService {
@Autowired
private MongoTemplate mongoTemplate;
@Async//异步底层就是开启一个新的线程
public CompletableFuture<String> saveTimeLine(Long userId, ObjectId publishId){
try {
List<Users> userList = mongoTemplate.find(Query.query(Criteria.where("userId").is(userId)), Users.class);
if (CollUtil.isEmpty(userList)){
//返回成功
return CompletableFuture.completedFuture("ok");
}
TimeLine timeLine = new TimeLine();
for (Users users : userList) {
timeLine.setId(ObjectId.get());
timeLine.setDate(System.currentTimeMillis());
timeLine.setPublishId(publishId);
timeLine.setUserId(userId);
mongoTemplate.save(timeLine,"quanzi_time_line_"+users.getFriendId());
}
} catch (Exception e) {
log.error("写入时间线失败,userId="+userId+",publishId="+publishId,e);
//TODO 事务回滚
return CompletableFuture.completedFuture("error");
}
return CompletableFuture.completedFuture("ok");
}
}
第八章、joda工具,获取文件路径
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.9</version>
</dependency>
@RunWith(SpringRunner.class)
@SpringBootTest
public class TodayBestServiceTest {
// 获得文件路径
@Test
public void getFilePath() {
DateTime dateTime = new DateTime();
System.out.println("/images/" + dateTime.toString("yyyy")
+ "/" + dateTime.toString("MM") + "/"
+ dateTime.toString("dd"));
}
}
打印的数据为:/images/2021/06/11
第九章、http请求方式
http请求的第一种方式
@Autowired
private RestTemplate restTemplate;
String userJson = restTemplate.getForObject(url, String.class);
http请求的第二种方式 糊涂工具
HttpResponse response = HttpRequest.post(targetUrl)
.body(JSONUtil.toJsonStr(param))
.timeout(20000) //请求超时时间
.execute();
System.out.println(response);
if (!response.isOk()) {
log.error("刷新token失败~~~ ");
return null;
}
String jsonBody = response.body();
JSONObject jsonObject = JSONUtil.parseObj(jsonBody);
第十章、重试模块
需要在启动类加上注解:@EnableRetry
<!--Spring重试模块-->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
简单示例
@Service
public class RetryService {
@Retryable(value = RuntimeException.class, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 2))
public int execute(int max) {
int data = RandomUtil.randomInt(1, 99);
System.out.println("生成:" + data);
if (data < max) {
throw new RuntimeException();
}
return data;
}
@Recover //全部重试失败后执行
public int recover(Exception e) {
System.out.println("全部重试完成。。。。。");
return 88; //返回默认
}
}
第十一章、通用请求服务,四种基本请求
package com.tanhua.dubbo.server.service;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.Method;
import com.tanhua.dubbo.server.exception.UnauthorizedException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
/**
* 通用请求服务
*/
@Service
@Slf4j
public class RequestService {
@Autowired
private TokenService tokenService;
/**
* 通用的发送请求方法
*
* @param url 请求地址
* @param body 请求参数
* @param method 请求方法
* @return
*/
@Retryable(value = UnauthorizedException.class, maxAttempts = 5, backoff = @Backoff(delay = 2000L, multiplier = 2))
public HttpResponse execute(String url, String body, Method method) {
String token = this.tokenService.getToken();
HttpRequest httpRequest;
switch (method) {
case POST: {
httpRequest = HttpRequest.post(url);
break;
}
case DELETE: {
httpRequest = HttpRequest.delete(url);
break;
}
case PUT: {
httpRequest = HttpRequest.put(url);
break;
}
case GET: {
httpRequest = HttpRequest.get(url);
break;
}
default: {
return null;
}
}
HttpResponse response = httpRequest
.header("Content-Type", "application/json") //设置请求内容类型
.header("Authorization", "Bearer " + token) //设置token
.body(body) // 设置请求数据
.timeout(20000) // 超时时间
.execute(); // 执行请求
if (response.getStatus() == 401) {
//token失效,重新刷新token
this.tokenService.refreshToken();
//抛出异常,需要进行重试
throw new UnauthorizedException(url, body, method);
}
return response;
}
@Recover //全部重试失败后执行
public HttpResponse recover(UnauthorizedException e) {
log.error("获取token失败!url = " + e.getUrl() + ", body = " + e.getBody() + ", method = " + e.getMethod().toString());
//如果重试5次后,依然不能获取到token,说明网络或账号出现了问题,只能返回null了,后续的请求将无法再执行
return null;
}
}
第十二章、枚举使用配合switch
package com.tanhua.common.enums;
public enum LikeType {
MUTUAL_LIKE("1"),LIKE("2"),FANS("3"),VISITOR("4"),NULL("null");
String type;
LikeType(String type) {
this.type = type;
}
public String getType() {
return type;
}
/*
* 匹配操作码
* */
public static LikeType matchOpCode(String type) {
for (LikeType likeType : LikeType.values()) {
if (likeType.type.equals(type))return likeType;
}
return LikeType.NULL;
}
}
switch (LikeType.matchOpCode(type)){
case MUTUAL_LIKE:{
break;
}
case LIKE:{
break;
}
case FANS:{
break;
}
case VISITOR:{
break;
}
default:
return pageResult;
}
第十三章、redis缓存解决方案
添加依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
定义@Cache注解,默认过期时间为60秒
package com.tanhua.server.utils;
import org.springframework.web.bind.annotation.GetMapping;
import java.lang.annotation.*;
/**
* 缓存注解,默认缓存时间为60秒,需要缓存的方法可以使用此注解
* 此注解只对标记了@GetMapping的方法有效
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Cache {
String value() default "60";
}
redis缓存拦截器,在进入方法之前进行拦截,如果有缓存就返回,没有就放行
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tanhua.server.utils.Cache;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static org.apache.commons.codec.digest.DigestUtils.md5Hex;
@Component
public class RedisCacheInterceptor implements HandlerInterceptor {
@Value("${cache.enable}")
private boolean enable;//全局开关
@Autowired
private RedisTemplate<String,String> redisTemplate;
private static final ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!enable)return true;//如果关闭开关直接放行
if (!(handler instanceof HandlerMethod)) return true;//如果处理器不是处理@RequestMapping方法的直接放行
if (!((HandlerMethod) handler).hasMethodAnnotation(GetMapping.class)) return true;//如果不是处理get请求的直接放行
if (!((HandlerMethod) handler).hasMethodAnnotation(Cache.class))return true;
//检查缓存中是否有响应
String redisKey = createRedisKey(request);
String resp = redisTemplate.opsForValue().get(redisKey);
if (StringUtils.isEmpty(resp))return true;//缓存未命中,放行
//缓存命中,响应数据
response.setCharacterEncoding("UTF-8");//设置字符编码
response.setContentType("application/json; charset=utf-8");//设置响应类型为json
response.getWriter().write(resp);
return false;
}
/**
* 创建redisKey,规则SERVER_CACHE_DATA_MD5(url + param + token)
* @param request 请求参数
* @return redisKey
*/
private String createRedisKey(HttpServletRequest request) throws JsonProcessingException {
String url = request.getRequestURI();//url
String param = objectMapper.writeValueAsString(request.getParameterMap());//获取param
String token = request.getHeader("Authorization");//获取token
return "SERVER_CACHE_DATA_"+md5Hex(url + param + token);
}
}
注册拦截器到Spring容器,这里将userTokenInterceptor也注册了
package com.tanhua.server.config;
import com.tanhua.server.interceptor.RedisCacheInterceptor;
import com.tanhua.server.interceptor.UserTokenInterceptor;
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 WebConfig implements WebMvcConfigurer {
@Autowired
private RedisCacheInterceptor redisCacheInterceptor;
@Autowired
private UserTokenInterceptor userTokenInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//考虑拦截器的顺序
registry.addInterceptor(this.userTokenInterceptor).addPathPatterns("/**");
registry.addInterceptor(this.redisCacheInterceptor).addPathPatterns("/**");
}
}
将响应结果写入缓存
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tanhua.server.utils.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.util.concurrent.TimeUnit;
@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice {
@Value("${cache.enable}")
private boolean enable;
private final RedisTemplate<String, String> redisTemplate;
private static final ObjectMapper MAPPER = new ObjectMapper();
@Autowired
public MyResponseBodyAdvice(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
//判断方法是否符合要求
return enable && returnType.hasMethodAnnotation(GetMapping.class) && returnType.hasMethodAnnotation(Cache.class);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
try {
String redisValue;//定义redis的值
if (body instanceof String) {//强转为String
redisValue = (String) body;
} else {//如果不是String类型就转为String类型
redisValue = MAPPER.writeValueAsString(body);
}
//生成redisKey,规则SERVER_CACHE_DATA_MD5(url + param + token)
String redisKey = RedisCacheInterceptor.createRedisKey(((ServletServerHttpRequest) request).getServletRequest());
//缓存的时间单位是秒
this.redisTemplate.opsForValue().set(
redisKey, //key
redisValue, //value
Long.valueOf(//超时时间
returnType.getMethodAnnotation(Cache.class).value()
),
TimeUnit.SECONDS//时间单位
);
} catch (Exception e) {
e.printStackTrace();
}
return body;
}
}
第十四章、ThreadLocal
编写UserThreadLocal
package com.tanhua.common.utils;
import com.tanhua.common.pojo.User;
public class UserThreadLocal {
private static final ThreadLocal<User> LOCAL = new ThreadLocal<>();
private UserThreadLocal(){
}
/**
* 将对象放入到ThreadLocal
*
* @param user
*/
public static void set(User user){
LOCAL.set(user);
}
/**
* 返回当前线程中的User对象
*
* @return
*/
public static User get(){
return LOCAL.get();
}
/**
* 删除当前线程中的User对象
*/
public static void remove(){
LOCAL.remove();
}
}
编写tokenInterceptor,拦截token,将user放入UserThreadLocal中
package com.tanhua.server.interceptor;
import cn.hutool.core.util.StrUtil;
import com.tanhua.common.pojo.User;
import com.tanhua.common.utils.NoAuthorization;
import com.tanhua.common.utils.UserThreadLocal;
import com.tanhua.server.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class UserTokenInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//校验handler是否是HandlerMethod
if (!(handler instanceof HandlerMethod)) {
return true;
}
//判断是否包含@NoAuthorization注解,如果包含,直接放行
if (((HandlerMethod) handler).hasMethodAnnotation(NoAuthorization.class)) {
return true;
}
//从请求头中获取token
String token = request.getHeader("Authorization");
if(StrUtil.isNotEmpty(token)){
User user = this.userService.queryUserByToken(token);
if(user != null){
//token有效
//将User对象放入到ThreadLocal中
UserThreadLocal.set(user);
return true;
}
}
//token无效,响应状态为401
response.setStatus(401); //无权限
return false;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//从ThreadLocal中移除User对象
UserThreadLocal.remove();
}
}
放行注解,标记此注解的类不需要检查token
package com.tanhua.common.utils;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented //标记注解
public @interface NoAuthorization {
}
//最后,拦截器也需要注册到容器中
@Autowired
private UserTokenInterceptor userTokenInterceptor;
registry.addInterceptor(this.userTokenInterceptor).addPathPatterns("/**");
第十五章、jwt的token实现
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
package com.tanhua.sso.service;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.DefaultJwsHeader;
import io.jsonwebtoken.impl.DefaultJwtBuilder;
import org.joda.time.DateTime;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestJWT {
//密钥不能太短
private String secret = "76bd425b6f29f7fcc2e0bfc286043df1";
/**
* 生成token
*/
@Test
public void createToken(){
Map<String, Object> header = new HashMap<>();
header.put(JwsHeader.TYPE,JwsHeader.JWT_TYPE);
header.put(JwsHeader.ALGORITHM,"HS256");
Map<String, Object> claims = new HashMap<>();
claims.put("id","2");
DefaultJwtBuilder jwtBuilder = new DefaultJwtBuilder();
String compact = jwtBuilder
.setHeader(header)
.setClaims(claims)
.signWith(SignatureAlgorithm.HS256, secret) //设置加密方法和加密盐
.setExpiration(new DateTime().plusHours(12).toDate())//设置过期时间为12小时
.compact();
System.out.println(compact);
}
/**
* 解析token
*/
@Test
public void testDecodeToken(){
String token ="eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTAzLCJleHAiOjE2MjE4NjgwNTN9.N_zHQlpZZLFwRb6Kie-RLV2Ao5FdiqVzH-JYUJKQ5Rc";
try {
// 通过token解析数据
Map<String, Object> body = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
System.out.println(body); //{mobile=1333333333, id=2, exp=1605513392}
} catch (ExpiredJwtException e) {
System.out.println("token已经过期!");
} catch (Exception e) {
System.out.println("token不合法!");
}
}
}
第十六章、单点登录
第十七章、fastDFS
它是一个分布式文件系统(DFS),具有文件的存取功能和同步功能,主要是用来解决海量数据存储问题。他有三个角色,分别是跟踪服务器trackerServer、存储服务器storageServer、客户端client。跟踪服务器是起调度作用的,他负责管理存储服务器和分组,存储服务器在启动后会连接到跟踪服务器,并且提供自己的分组等信息,保持周期性心跳。存储服务器主要用来存储和备份服务,他们是以组为单位的,同一个的组的存储服务器数据是互相备份的。客户端就是我们所使用的服务器,用来连接客户进行上传下载文件。
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>1.26.7</version>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
# ===================================================================
# 分布式文件系统FDFS配置
# ===================================================================
fdfs.so-timeout = 1501
fdfs.connect-timeout = 601
#缩略图生成参数
fdfs.thumb-image.width= 150
fdfs.thumb-image.height= 150
#TrackerList参数,支持多个
fdfs.tracker-list=192.168.31.81:22122
#访问路径
fdfs.web-server-url=http://192.168.31.81:8888/
package com.tanhua.server;
import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.File;
import java.io.IOException;
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestFastDFS {
@Autowired
protected FastFileStorageClient storageClient;
@Autowired
private FdfsWebServer fdfsWebServer;
@Test
public void testUpload(){
String path = "F:\\1.jpg";
File file = new File(path);
try {
StorePath storePath = this.storageClient.uploadFile(FileUtils.openInputStream(file), file.length(), "jpg", null);
System.out.println(storePath); //StorePath [group=group1, path=M00/00/00/wKgfUV2GJSuAOUd_AAHnjh7KpOc1.1.jpg]
System.out.println(fdfsWebServer.getWebServerUrl() + storePath.getFullPath());//group1/M00/00/00/wKgfUV2GJSuAOUd_AAHnjh7KpOc1.1.jpg
} catch (IOException e) {
e.printStackTrace();
}
}
}