5.登录与分页功能分析

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上

相关推荐
LinMINGJing0071 小时前
简单web服务器示例图
后端
今天AI了吗1 小时前
AI Agent 在数据分析领域的落地判断:哪些场景真的需要 Agent
java·数据库·人工智能·python·sql·数据分析·copilot
SimonKing1 小时前
SwitchHosts V5大改版,我发现了这些惊喜和坑
java·后端·程序员
坤岭1 小时前
企业级RAG全链路
后端
Csvn1 小时前
📊 SQL 入门 Day 23:存储过程与函数
后端·sql
weixin_431600441 小时前
Agent Workflow 学习向:最小拖拽画布,看得见地编排,再一键跑通
后端·学习·ai·dify
IT_陈寒2 小时前
Python的列表拷贝坑得我原地打转
前端·人工智能·后端
山甫aa2 小时前
日志技术 Logback + Slf4j —— 从零开始的 Web 后端学习
java·后端·学习·web·logback
韶博雅2 小时前
开启补充日志
java·开发语言·sql