交友app总结


前言

记录了交友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();
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值