jwt登录
- 用户登录之后,将用户信息写入token颁发给用户。
- 用户的所有请求会携带这个token。
- 因为每个请求都会先经过网关,所以在网关服务中做登录校验。而不是每个微服务中去反复多次做登录校验。
- 将用户信息存入请求头发送给其他微服务。



ini
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1.获取请求request信息
ServerHttpRequest request = exchange.getRequest();
String method = request.getMethodValue();
String path = request.getPath().toString();
String antPath = method + ":" + path;
// 2.判断是否是无需登录的路径
if(isExcludePath(antPath)){
// 直接放行
return chain.filter(exchange);
}
// 3.尝试获取用户信息
List<String> authHeaders = exchange.getRequest().getHeaders().get(AUTHORIZATION_HEADER);
String token = authHeaders == null ? "" : authHeaders.get(0);
R<LoginUserDTO> r = authUtil.parseToken(token);
// 4.如果用户是登录状态,尝试更新请求头,传递用户信息
if(r.success()){
exchange.mutate()
.request(builder -> builder.header(USER_HEADER, r.getData().getUserId().toString()))
.build();
}

java
package com.tianji.authsdk.resource.interceptors;
import com.tianji.auth.common.constants.JwtConstants;
import com.tianji.common.utils.UserContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class UserInfoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1.尝试获取头信息中的用户信息
String authorization = request.getHeader(JwtConstants.USER_HEADER);
// 2.判断是否为空
if (authorization == null) {
return true;
}
// 3.转为用户id并保存
try {
Long userId = Long.valueOf(authorization);
UserContext.setUser(userId);
return true;
} catch (NumberFormatException e) {
log.error("用户身份信息格式不正确,{}, 原因:{}", authorization, e.getMessage());
return true;
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 清理用户信息
UserContext.removeUser();
}
}
csharp
package com.tianji.common.utils;
public class UserContext {
private static final ThreadLocal<Long> TL = new ThreadLocal<>();
/**
* 保存用户信息
* @param userId 用户id
*/
public static void setUser(Long userId){
TL.set(userId);
}
/**
* 获取用户
* @return 用户id
*/
public static Long getUser(){
return TL.get();
}
/**
* 移除用户信息
*/
public static void removeUser(){
TL.remove();
}
}
PageQuery对象
ini
package com.tianji.common.domain.query;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.common.constants.Constant;
import com.tianji.common.utils.StringUtils;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
@Data
@ApiModel(description = "分页请求参数")
@Accessors(chain = true)
public class PageQuery {
public static final Integer DEFAULT_PAGE_SIZE = 20;
public static final Integer DEFAULT_PAGE_NUM = 1;
@ApiModelProperty(value = "页码", example = "1")
@Min(value = 1, message = "页码不能小于1")
private Integer pageNo = DEFAULT_PAGE_NUM;
@ApiModelProperty(value = "每页大小", example = "5")
@Min(value = 1, message = "每页查询数量不能小于1")
private Integer pageSize = DEFAULT_PAGE_SIZE;
@ApiModelProperty(value = "是否升序", example = "true")
private Boolean isAsc = true;
@ApiModelProperty(value = "排序字段", example = "id")
private String sortBy;
public int from(){
return (pageNo - 1) * pageSize;
}
public <T> Page<T> toMpPage(OrderItem ... orderItems) {
Page<T> page = new Page<>(pageNo, pageSize);
// 是否手动指定排序方式
if (orderItems != null && orderItems.length > 0) {
for (OrderItem orderItem : orderItems) {
page.addOrder(orderItem);
}
return page;
}
// 前端是否有排序字段
if (StringUtils.isNotEmpty(sortBy)){
OrderItem orderItem = new OrderItem();
orderItem.setAsc(isAsc);
orderItem.setColumn(sortBy);
page.addOrder(orderItem);
}
return page;
}
public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc) {
if (StringUtils.isBlank(sortBy)){
sortBy = defaultSortBy;
this.isAsc = isAsc;
}
Page<T> page = new Page<>(pageNo, pageSize);
OrderItem orderItem = new OrderItem();
orderItem.setAsc(this.isAsc);
orderItem.setColumn(sortBy);
page.addOrder(orderItem);
return page;
}
public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {
return toMpPage(Constant.DATA_FIELD_NAME_CREATE_TIME, false);
}
}
分页功能
ini
@Override
public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
//获取登录用户
Long userId = UserContext.getUser();
//根据登录用户做分页查询
Page<LearningLesson> page = lambdaQuery().eq(LearningLesson::getUserId, userId)
.page(query.toMpPage("latest_learn_time", false));
List<LearningLesson> records = page.getRecords();
if(CollUtils.isEmpty(records)) {
return PageDTO.empty(page);
}
Set<Long> courseIdSet = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
//查询课程信息
List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseIdSet);
if (CollUtils.isEmpty(simpleInfoList)) {
throw new BadRequestException("课程信息不存在");
}
Map<Long, CourseSimpleInfoDTO> cMap = simpleInfoList.stream()
.collect(Collectors.toMap(CourseSimpleInfoDTO::getId, e -> e));
//封装VO返回
List<LearningLessonVO> list = new ArrayList<>(records.size());
for (LearningLesson learningLesson: records) {
LearningLessonVO learningLessonVO = BeanUtils.copyBean(learningLesson, LearningLessonVO.class);
CourseSimpleInfoDTO courseSimpleInfoDTO = cMap.get(learningLessonVO.getCourseId());
learningLessonVO.setCourseName(courseSimpleInfoDTO.getName());
learningLessonVO.setCourseCoverUrl(courseSimpleInfoDTO.getCoverUrl());
learningLessonVO.setSections(courseSimpleInfoDTO.getSectionNum());
list.add(learningLessonVO);
}
return PageDTO.of(page, list);
}
PageDTO
java
package com.tianji.common.domain.dto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.Convert;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "分页结果")
public class PageDTO<T> {
@ApiModelProperty("总条数")
protected Long total;
@ApiModelProperty("总页码数")
protected Long pages;
@ApiModelProperty("当前页数据")
protected List<T> list;
public static <T> PageDTO<T> empty(Long total, Long pages) {
return new PageDTO<>(total, pages, CollUtils.emptyList());
}
public static <T> PageDTO<T> empty(Page<?> page) {
return new PageDTO<>(page.getTotal(), page.getPages(), CollUtils.emptyList());
}
public static <T> PageDTO<T> of(Page<T> page) {
if(page == null){
return new PageDTO<>();
}
if (CollUtils.isEmpty(page.getRecords())) {
return empty(page);
}
return new PageDTO<>(page.getTotal(), page.getPages(), page.getRecords());
}
public static <T,R> PageDTO<T> of(Page<R> page, Function<R, T> mapper) {
if(page == null){
return new PageDTO<>();
}
if (CollUtils.isEmpty(page.getRecords())) {
return empty(page);
}
return new PageDTO<>(page.getTotal(), page.getPages(),
page.getRecords().stream().map(mapper).collect(Collectors.toList()));
}
public static <T> PageDTO<T> of(Page<?> page, List<T> list) {
return new PageDTO<>(page.getTotal(), page.getPages(), list);
}
public static <T, R> PageDTO<T> of(Page<R> page, Class<T> clazz) {
return new PageDTO<>(page.getTotal(), page.getPages(), BeanUtils.copyList(page.getRecords(), clazz));
}
public static <T, R> PageDTO<T> of(Page<R> page, Class<T> clazz, Convert<R, T> convert) {
return new PageDTO<>(page.getTotal(), page.getPages(), BeanUtils.copyList(page.getRecords(), clazz, convert));
}
@ApiModelProperty(hidden = true)
@JsonIgnore
public boolean isEmpty(){
return list == null || list.size() == 0;
}
}

表示当前分支为feature-lessons

表示将dev分支merge到当前分值 feature-lessons上