学习: 尚硅谷Java项目之尚庭公寓(2)

三、 后台管理系统后端开发

3.租赁管理

看房预约管理

根据条件分页查询预约信息

Alt+Enter,实现方法

创建方法、Generate statement

XML 复制代码
    <resultMap id="AppointmentVoMap" type="org.example.lease.web.admin.vo.appointment.AppointmentVo" autoMapping="true">
        <id property="id" column="id"/>
        <association property="apartmentInfo" javaType="org.example.lease.model.entity.ApartmentInfo"
                     autoMapping="true">
            <id property="id" column="apartment_id"/>
            <result property="name" column="apartment_name"/>
        </association>
    </resultMap>
    <select id="pageAppointmentByQuery" resultMap="AppointmentVoMap">
        select va.id,
               va.user_id,
               va.name,
               va.phone,
               va.appointment_time,
               va.additional_info,
               va.appointment_status,
               ai.id   apartment_id,
               ai.name apartment_name,
               ai.district_id,
               ai.district_name,
               ai.city_id,
               ai.city_name,
               ai.province_id,
               ai.province_name
        from view_appointment va
                 left join
             apartment_info ai
             on va.apartment_id = ai.id and ai.is_deleted = 0
        <where>
            va.is_deleted = 0
            <if test="queryVo.provinceId != null">
                and ai.province_id = #{queryVo.provinceId}
            </if>
            <if test="queryVo.cityId != null">
                and ai.city_id = #{queryVo.cityId}
            </if>
            <if test="queryVo.districtId != null">
                and ai.district_id = #{queryVo.districtId}
            </if>
            <if test="queryVo.apartmentId != null">
                and va.apartment_id = #{queryVo.apartmentId}
            </if>
            <if test="queryVo.name != null and queryVo.name != ''">
                and va.name like concat('%',#{queryVo.name},'%')
            </if>
            <if test="queryVo.phone != null and queryVo.phone != ''">
                and va.phone like concat('%',#{queryVo.phone},'%')
            </if>
        </where>
    </select>

成功

设置序列化后的时间字符串

格式

XML 复制代码
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")

时区

XML 复制代码
@JsonFormat(timezone = "GMT+8")
XML 复制代码
spring:
  jackson:
    time-zone: GMT+8
根据ID更新预约状态

成功

租约管理

保存获更新租约信息
根据条件分页查询租约列表

Alt+Enter,实现方法

创建方法、Generate statement

XML 复制代码
   <resultMap id="agreementVoMap" type="org.example.lease.web.admin.vo.agreement.AgreementVo" autoMapping="true">
        <id property="id" column="id"/>
        <association property="apartmentInfo" javaType="org.example.lease.model.entity.ApartmentInfo"
                     autoMapping="true">
            <id property="id" column="apartment_id"/>
            <result property="name" column="apartment_name"/>
        </association>
        <association property="roomInfo" javaType="org.example.lease.model.entity.RoomInfo" autoMapping="true">
            <id property="id" column="room_id"/>
        </association>
        <association property="paymentType" javaType="org.example.lease.model.entity.PaymentType" autoMapping="true">
            <id property="id" column="payment_type_id"/>
            <result property="name" column="payment_type_name"/>
        </association>
        <association property="leaseTerm" javaType="org.example.lease.model.entity.LeaseTerm" autoMapping="true">
            <id property="id" column="lease_term_id"/>
        </association>
    </resultMap>

    <select id="pageAgreementByQuery" resultMap="agreementVoMap">
        select la.id,
               la.phone,
               la.name,
               la.identification_number,
               la.lease_start_date,
               la.lease_end_date,
               la.rent,
               la.deposit,
               la.status,
               la.source_type,
               la.additional_info,
               ai.id   apartment_id,
               ai.name apartment_name,
               ai.district_id,
               ai.district_name,
               ai.city_id,
               ai.city_name,
               ai.province_id,
               ai.province_name,
               ri.id   room_id,
               ri.room_number,
               pt.id   payment_type_id,
               pt.name payment_type_name,
               pt.pay_month_count,
               lt.id   lease_term_id,
               lt.month_count,
               lt.unit
        from lease_agreement la
                 left join
             apartment_info ai
             on la.apartment_id = ai.id and ai.is_deleted = 0
                 left join
             room_info ri
             on la.room_id = ri.id and ri.is_deleted = 0
                 left join
             payment_type pt
             on la.payment_type_id = pt.id and pt.is_deleted = 0
                 left join
             lease_term lt
             on la.lease_term_id = lt.id and lt.is_deleted = 0
        <where>
            la.is_deleted = 0
            <if test="queryVo.provinceId != null">
                and ai.province_id = #{queryVo.provinceId}
            </if>
            <if test="queryVo.cityId != null">
                and ai.city_id = #{queryVo.cityId}
            </if>
            <if test="queryVo.districtId != null">
                and ai.district_id = #{queryVo.districtId}
            </if>
            <if test="queryVo.apartmentId != null">
                and la.apartment_id = #{queryVo.apartmentId}
            </if>
            <if test="queryVo.roomNumber != null and queryVo.roomNumber != ''">
                and ri.room_number like concat('%',#{queryVo.roomNumber},'%')
            </if>
            <if test="queryVo.name != null and queryVo.name != ''">
                and la.name like concat('%',#{queryVo.name},'%')
            </if>
            <if test="queryVo.phone != null and queryVo.phone != ''">
                and la.phone like concat('%',#{queryVo.phone},'%')
            </if>
        </where>
    </select>
根据ID查询租约信息

Alt+Enter,实现方法

java 复制代码
public class LeaseAgreementServiceImpl extends ServiceImpl<LeaseAgreementMapper, LeaseAgreement>
        implements LeaseAgreementService {

    @Autowired
    private LeaseAgreementMapper leaseAgreementMapper;

    @Autowired
    private ApartmentInfoMapper apartmentInfoMapper;

    @Autowired
    private RoomInfoMapper roomInfoMapper;

    @Autowired
    private PaymentTypeMapper paymentTypeMapper;

    @Autowired
    private LeaseTermMapper leaseTermMapper;


    @Override
    public IPage<AgreementVo> pageAgreementByQuery(Page<AgreementVo> page, AgreementQueryVo queryVo) {
        return leaseAgreementMapper.pageAgreementByQuery(page, queryVo);
    }

    @Override
    public AgreementVo getAgreementById(Long id) {
        //1.查询租约信息
        LeaseAgreement leaseAgreement = leaseAgreementMapper.selectById(id);
        //2.查询公寓信息
        ApartmentInfo apartmentInfo = apartmentInfoMapper.selectById(leaseAgreement.getApartmentId());
        //3.查询房间信息
        RoomInfo roomInfo = roomInfoMapper.selectById(leaseAgreement.getRoomId());
        //4.查询支付方式
        PaymentType paymentType = paymentTypeMapper.selectById(leaseAgreement.getPaymentTypeId());
        //5.查询租期
        LeaseTerm leaseTerm = leaseTermMapper.selectById(leaseAgreement.getLeaseTermId());
        //6.组合
        AgreementVo agreementVo = new AgreementVo();
        BeanUtils.copyProperties(leaseAgreement, agreementVo);
        agreementVo.setApartmentInfo(apartmentInfo);
        agreementVo.setRoomInfo(roomInfo);
        agreementVo.setPaymentType(paymentType);
        agreementVo.setLeaseTerm(leaseTerm);
        return agreementVo;
    }
}
根据ID删除租约信息

成功

根据ID更新租约状态

成功

定时检查租约状态

4.用户管理

java 复制代码
@Autowired
private UserInfoService userInfoService;

根据条件分页查询用户列表

条件:手机号码、用户账号状态

java 复制代码
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(queryVo.getPhone()!= null,UserInfo::getPhone, queryVo.getPhone());
        queryWrapper.eq(queryVo.getStatus()!= null,UserInfo::getStatus, queryVo.getStatus());

分页查询

java 复制代码
   Page<UserInfo> page = new Page<>(current, size);
   ...
   Page<UserInfo> list = userInfoService.page(page, queryWrapper);
   return Result.ok(list);

测试

根据ID更新用户状态

java 复制代码
    @Operation(summary = "根据用户id更新账号状态")
    @PostMapping("updateStatusById")
    public Result updateStatusById(@RequestParam Long id, @RequestParam BaseStatus status) {
        LambdaUpdateWrapper<UserInfo> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(UserInfo::getId, id);
        updateWrapper.set(UserInfo::getStatus, status);
        userInfoService.update(updateWrapper);
        return Result.ok();
    }

5.系统管理

后台用户岗位管理

SystemPostController.java

java 复制代码
  @Autowired
private SystemPostService systemPostService;
分页查询岗位信息
java 复制代码
    @Operation(summary = "分页获取岗位信息")
    @GetMapping("page")
    private Result<IPage<SystemPost>> page(@RequestParam long current, @RequestParam long size) {
        IPage<SystemPost> page = new Page<>(current, size);
        IPage<SystemPost> list = systemPostService.page(page);
        return Result.ok(list);
    }

测试

保存或更新岗位信息
java 复制代码
    @Operation(summary = "保存或更新岗位信息")
    @PostMapping("saveOrUpdate")
    public Result saveOrUpdate(@RequestBody SystemPost systemPost) {
        systemPostService.saveOrUpdate(systemPost);
        return Result.ok();
    }
根据ID删除岗位信息
java 复制代码
    @DeleteMapping("deleteById")
    @Operation(summary = "根据id删除岗位")
    public Result removeById(@RequestParam Long id) {
        systemPostService.removeById(id);
        return Result.ok();
    }

测试

获取全部岗位列表
java 复制代码
    @Operation(summary = "获取全部岗位列表")
    @GetMapping("list")
    public Result<List<SystemPost>> list() {
        List<SystemPost> list = systemPostService.list();
        return Result.ok(list);
    }
根据ID获取岗位信息
java 复制代码
    @GetMapping("getById")
    @Operation(summary = "根据id获取岗位信息")
    public Result<SystemPost> getById(@RequestParam Long id) {
        SystemPost systemPost = systemPostService.getById(id);
        return Result.ok(systemPost);
    }
根据ID修改岗位状态
java 复制代码
    @Operation(summary = "根据岗位id修改状态")
    @PostMapping("updateStatusByPostId")
    public Result updateStatusByPostId(@RequestParam Long id, @RequestParam BaseStatus status) {
        LambdaUpdateWrapper<SystemPost> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(SystemPost::getId, id)
                .set(SystemPost::getStatus, status);
        return Result.ok();
    }

后台用户信息管理

java 复制代码
  @Autowired
    private SystemUserService systemUserService;
根据条件分页查询后台用户列表

用户和岗位表

java 复制代码
    @Operation(summary = "根据条件分页查询后台用户列表")
    @GetMapping("page")
    public Result<IPage<SystemUserItemVo>> page(@RequestParam long current, @RequestParam long size, SystemUserQueryVo queryVo) {
        IPage<SystemUser> page = new Page<>(current, size);
        IPage<SystemUserItemVo> list =  systemUserService.pageSystemUserByQuery(page, queryVo);
        return Result.ok(list);
    }

alt+enter、实现方法

SystemUserServiceImpl.java

java 复制代码
 @Autowired
    private SystemUserMapper systemUserMapper;

    @Override
    public IPage<SystemUserItemVo> pageSystemUserByQuery(IPage<SystemUser> page, SystemUserQueryVo queryVo) {
        return systemUserMapper.pageSystemUserByQuery(page, queryVo);
    }

alt+enter、Generate statement

java 复制代码
<select id="pageSystemUserByQuery"
            resultType="org.example.lease.web.admin.vo.system.user.SystemUserItemVo">
        select su.id,
               username,
               su.name,
               type,
               phone,
               avatar_url,
               additional_info,
               post_id,
               su.status,
               sp.name post_name
        from system_user su
                 left join system_post sp on su.post_id = sp.id and sp.is_deleted = 0
        <where>
            su.is_deleted = 0
            <if test="queryVo.name != null and queryVo.name != ''">
                and su.name like concat('%',#{queryVo.name},'%')
            </if>
            <if test="queryVo.phone !=null and queryVo.phone != ''">
                and su.phone like concat('%',#{queryVo.phone},'%')
            </if>
        </where>
    </select>

测试

根据ID查询后台用户信息
java 复制代码
    @Operation(summary = "根据ID查询后台用户信息")
    @GetMapping("getById")
    public Result<SystemUserItemVo> getById(@RequestParam Long id) {
        SystemUser list = systemUserService.getSystemUserById(id);
        return Result.ok();
    }

alt+enter、实现方法

SystemUserServiceImpl.java

java 复制代码
/**
     * 根据用户ID获取系统用户信息
     *
     * @param id 用户ID
     * @return SystemUserItemVo 包含用户信息的视图对象
     */
    @Override
    public SystemUserItemVo getSystemUserById(Long id) {
        // 通过ID查询系统用户信息
        SystemUser systemUser = systemUserMapper.selectById(id);
        // 根据用户中的职位ID查询职位信息
        SystemPost systemPost = systemPostMapper.selectById(systemUser.getPostId());

        // 创建用户信息视图对象
        SystemUserItemVo systemUserItemVo = new SystemUserItemVo();
        // 将职位信息复制到用户信息视图对象中
        BeanUtils.copyProperties(systemPost, systemUserItemVo);
        // 设置职位名称
        systemUserItemVo.setPostName(systemUserItemVo.getPostName());

        // 返回包含用户信息的视图对象
        return systemUserItemVo;
    }
保存或更新后台用户信息

密码通常会使用一些单向函数进行处理,常用于处理密码的单向函数(算法)有MD5、SHA-256等

Apache Commons是Apache软件基金会下的一个项目,其致力于提供可重用的开源软件,其中包含了很多易于使用的现成工具。

Apache Commons 提供了一个工具类DigestUtils

common模块的pom.xml中添加启动器

java 复制代码
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
</dependency>
java 复制代码
DigestUtils.md5Hex(systemUser.getPassword())
  • DigestUtils: Apache Commons Codec 提供的工具类,用于计算消息摘要(哈希值)
  • md5Hex(): 方法将输入字符串转换为32位的小写MD5哈希值
  • systemUser.getPassword(): 获取用户的密码
java 复制代码
systemUser.setPassword()// 设置加密后的密码

整体

java 复制代码
    @Operation(summary = "保存或更新后台用户信息")
    @PostMapping("saveOrUpdate")
    public Result saveOrUpdate(@RequestBody SystemUser systemUser) {
        if (systemUser.getPassword() != null) {
            systemUser.setPassword(DigestUtils.md5Hex(systemUser.getPassword()));
        }
        systemUserService.saveOrUpdate(systemUser);
        return Result.ok();
    }

Mybatis-Plus 更新策略

使用Mybatis-Plus提供的更新方法时,若实体中的字段为null,默认情况下,最终生成的update语句中,不会包含该字段。若想改变默认行为,可做以下配置:

全局配置

application.yml中配置如下参数

复制代码
mybatis-plus:
  global-config:
    db-config:
      update-strategy: <strategy>
  • ignore:忽略空值判断,不管字段是否为空,都会进行更新

  • not_null:进行非空判断,字段非空才会进行判断,默认值

  • not_empty:进行非空判断,并进行非空串("")判断,主要针对字符串类型

  • never:从不进行更新,不管该字段为何值,都不更新

  • strategy:可选值

局部配置

  • 在实体类中的具体字段通过@TableField注解进行配置,如下:

    复制代码
    @Schema(description = "密码")
    @TableField(value = "password", updateStrategy = FieldStrategy.NOT_EMPTY)
    private String password;

添加成功

判断后台用户名是否可用
java 复制代码
    @Operation(summary = "判断后台用户名是否可用")
    @GetMapping("isUserNameAvailable")
    public Result<Boolean> isUsernameExists(@RequestParam String username) {
        LambdaQueryWrapper<SystemUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SystemUser::getUsername, username);
        long count = systemUserService.count(queryWrapper);
        return Result.ok(count == 0);
    }
根据ID删除后台用户信息
java 复制代码
    @DeleteMapping("deleteById")
    @Operation(summary = "根据ID删除后台用户信息")
    public Result removeById(@RequestParam Long id) {
        systemUserService.removeById(id);
        return Result.ok();
    }
根据ID修改后台用户状态
java 复制代码
    @Operation(summary = "根据ID修改后台用户状态")
    @PostMapping("updateStatusByUserId")
    public Result updateStatusByUserId(@RequestParam Long id, @RequestParam BaseStatus status) {
        LambdaUpdateWrapper<SystemUser> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(SystemUser::getId, id).set(SystemUser::getStatus, status);
        systemUserService.update(updateWrapper);
        return Result.ok();
    }

6.登录管理

接口开发

LoginController.java

java 复制代码
    @Autowired
    private LoginService loginService;
获取图形验证码

验证码生成工具, 使用开源的验证码生成工具EasyCaptcha

common模块的pom.xml文件中增加如下启动器

复制代码
<dependency>
    <groupId>com.github.whvcse</groupId>
    <artifactId>easy-captcha</artifactId>
</dependency>

Redis

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

application.yml中增加如下配置

复制代码
spring:
  data:
    redis:
      host: <hostname>
      port: <port>
      database: 0

LoginController.java

java 复制代码
    @Operation(summary = "获取图形验证码")
    @GetMapping("login/captcha")
    public Result<CaptchaVo> getCaptcha() {
        CaptchaVo captcha = loginService.getCaptcha();
        return Result.ok(captcha);
    }

Alt+enter、实现方法

LoginServiceImpl.java

java 复制代码
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public CaptchaVo getCaptcha() {
        SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 4);

        String code = specCaptcha.text().toLowerCase();
        String key = "admin:login:" + UUID.randomUUID();
        String image = specCaptcha.toBase64();
        redisTemplate.opsForValue().set(key, code, 60, TimeUnit.SECONDS);

        return new CaptchaVo(image, key);
    }

为方便管理,可以将Reids相关的一些值定义为常量,例如key的前缀、TTL时长

common/src/main/java/org/example/lease/common/constant/RedisConstant.java

java 复制代码
public class RedisConstant {
    public static final String ADMIN_LOGIN_PREFIX = "admin:login:";
    public static final Integer ADMIN_LOGIN_CAPTCHA_TTL_SEC = 60;
    public static final String APP_LOGIN_PREFIX = "app:login:";
    public static final Integer APP_LOGIN_CODE_RESEND_TIME_SEC = 60;
    public static final Integer APP_LOGIN_CODE_TTL_SEC = 60 * 10;
    public static final String APP_ROOM_PREFIX = "app:room:";
}

LoginServiceImpl.java

java 复制代码
    @Override
    public CaptchaVo getCaptcha() {
        SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 4);

        String code = specCaptcha.text().toLowerCase();
        String key = RedisConstant.ADMIN_LOGIN_PREFIX + UUID.randomUUID();
        String image = specCaptcha.toBase64();
        redisTemplate.opsForValue().set(key, code, RedisConstant.ADMIN_LOGIN_CAPTCHA_TTL_SEC, TimeUnit.SECONDS);

        return new CaptchaVo(image, key);
    }
登录接口
  • 前端发送usernamepasswordcaptchaKeycaptchaCode请求登录。

LoginController.java

java 复制代码
    @Operation(summary = "登录")
    @PostMapping("login")
    public Result<String> login(@RequestBody LoginVo loginVo) {
        String token = loginService.login(loginVo);
        return Result.ok(token);
    }

alt+enter、实现方法

LoginServiceImpl.java

  • 判断captchaCode是否为空,若为空,则直接响应验证码为空;若不为空进行下一步判断。
java 复制代码
        //1.判断是否输入了验证码
        if (loginVo.getCaptchaCode() == null) {
            throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_NOT_FOUND);
        }
  • 根据captchaKey从Redis中查询之前保存的code,若查询出来的code为空,则直接响应验证码已过期;若不为空进行下一步判断。
java 复制代码
        //2.校验验证码
        String code = redisTemplate.opsForValue().get(loginVo.getCaptchaKey());

        if (code == null) {
            throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_EXPIRED);
        }
  • 比较captchaCodecode,若不相同,则直接响应验证码不正确;若相同则进行下一步判断。
java 复制代码
        if (!code.equals(loginVo.getCaptchaCode().toLowerCase())) {
            throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_ERROR);
        }
java 复制代码
    @Autowired
    private SystemUserMapper systemUserMapper;
  • 根据username查询数据库,若查询结果为空,则直接响应账号不存在;若不为空则进行下一步判断。
java 复制代码
        //3.校验用户是否存在
        LambdaQueryWrapper<SystemUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SystemUser::getUsername, loginVo.getUsername());
        SystemUser systemUser = systemUserMapper.selectOne(queryWrapper);
        if (systemUser == null) {
            throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_NOT_EXIST_ERROR);
        }
  • 查看用户状态,判断是否被禁用,若禁用,则直接响应账号被禁;若未被禁用,则进行下一步判断。
java 复制代码
        //4.校验用户是否被禁
        if (systemUser.getStatus() == BaseStatus.DISABLE) {
            throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_DISABLED_ERROR);
        }
  • 比对password和数据库中查询的密码,若不一致,则直接响应账号或密码错误,若一致则进行入最后一步。
java 复制代码
        //5.校验用户密码
        if (!systemUser.getPassword().equals(DigestUtils.md5Hex(loginVo.getPassword()))) {
            throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_ERROR);
        }
  • 创建JWT,并响应给浏览器。

引入Maven依赖

common模块的pom.xml文件中增加启动器

java 复制代码
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
</dependency>

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <scope>runtime</scope>
</dependency>

common/src/main/java/org/example/lease/common/utils/JwtUtil.java

java 复制代码
public static String creatToken(Long userId, String username) {
        SecretKey secretKey = Keys.hmacShaKeyFor("asdfghjkl".getBytes());
        String token = Jwts.builder()
                .setSubject("USER_INFO")
                .setExpiration(new Date(System.currentTimeMillis() + 3600000))
                .claim("userId", userId)
                .claim("username", username)
                .signWith(secretKey, SignatureAlgorithm.HS256)
                .compact();

        return token;

    }

测试

java 复制代码
    public static void main(String[] args) {
        System.out.println(creatToken(1l, "zhangsan"));
    }

JWT签名密钥的长度不符合安全要求

随机生成密码

LoginServiceImpl.java

java 复制代码
        return JwtUtil.creatToken(systemUser.getId(), systemUser.getUsername());

测试

原因

修改方法

alt+enter、Generate statement

java 复制代码
 select id,
               username,
               password,
               name,
               type,
               phone,
               avatar_url,
               additional_info,
               post_id,
               status
        from system_user
        where is_deleted = 0
          and username = #{username}

测试

编写HandlerInterceptor

我们需要为所有受保护的接口增加校验JWT合法性的逻辑。

JwtUtil.java

java 复制代码
public static void parseToken(String token) {
        try {
            JwtParser jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();
            jwtParser.parseClaimsJws(token).getBody();
        } catch (ExpiredJwtException e) {
            throw new LeaseException(ResultCodeEnum.TOKEN_EXPIRED);
        } catch (JwtException e) {
            throw new LeaseException(ResultCodeEnum.TOKEN_INVALID);
        }
    }

web-admin模块中创键

web/web-admin/src/main/java/org/example/lease/web/admin/custom/interceptor/AuthenticationInterceptor.java

java 复制代码
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("access-token");
        if (token == null) {
            throw new LeaseException(ResultCodeEnum.ADMIN_LOGIN_AUTH);
        }
        JwtUtil.parseToken(token);
        return true;
    }
}
java 复制代码
        if (token == null) {
            throw new LeaseException(ResultCodeEnum.ADMIN_LOGIN_AUTH);
        }

移到JwtUtil.java

Knife4j配置

在增加上述拦截器后,为方便继续调试其他接口,可以获取一个长期有效的Token,将其配置到Knife4j的全局参数中

java 复制代码
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this.authenticationInterceptor);
    }

测试

控制拦截器

WebMvcConfiguration.java

java 复制代码
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this.authenticationInterceptor).addPathPatterns("/admin/**");
    }

测试

控制拦截器

WebMvcConfiguration.java

java 复制代码
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this.authenticationInterceptor).addPathPatterns("/admin/**").excludePathPatterns("/admin/login/**");
    }

测试

发送请求时会自动携带该header

生成测试用jwt

JwtUtil.java

java 复制代码
    public static void main(String[] args) {
        System.out.println(creatToken(2L, "user"));
    }
java 复制代码
 .setExpiration(new Date(System.currentTimeMillis() + 3600000*24*365L))

测试

需要每个分组设置全局参数

成功

获取登录用户个人信息

需要从Jwt中获取用户id

JwtUtil.java

LoginController.java

java 复制代码
    @Operation(summary = "获取登陆用户个人信息")
    @GetMapping("info")
    public Result<SystemUserInfoVo> info(@RequestHeader("access-token") String token) {
        Claims claims = JwtUtil.parseToken(token);
        Long userId = claims.get("userId", Long.class);
        SystemUserInfoVo userInfo = loginService.getLoginUserInfoById(userId);
        return Result.ok(userInfo);
    }

alt+enter、实现方法

java 复制代码
    @Override
    public SystemUserInfoVo getLoginUserInfoById(Long userId) {
        SystemUser systemUser = systemUserMapper.selectById(userId);
        SystemUserInfoVo systemUserInfoVo = new SystemUserInfoVo();
        systemUserInfoVo.setName(systemUser.getName());
        systemUserInfoVo.setAvatarUrl(systemUser.getAvatarUrl());
        return systemUserInfoVo;
    }

JWT会被重复解析两次,在拦截器将Token解析完毕后,将结果保存至ThreadLocal

common模块中创建common/src/main/java/org/example/lease/common/login/LoginUserHolder.java

java 复制代码
package org.example.lease.common.login;

public class LoginUserHolder {
    public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();

    public static void setLoginUser(LoginUser loginUser) {
        threadLocal.set(loginUser);
    }

    public static LoginUser getLoginUser() {
        return threadLocal.get();
    }

    public static void clear() {
        threadLocal.remove();
    }
}

common/src/main/java/org/example/lease/common/login/LoginUser.java

java 复制代码
package org.example.lease.common.login;


import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class LoginUser {

    private Long userId;
    private String username;
}

修改AuthenticationInterceptor拦截器

java 复制代码
@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("access-token");
        Claims clamis = JwtUtil.parseToken(token);
        Long userId = clamis.get("userId", Long.class);
        String username = clamis.get("username", String.class);
        LoginUserHolder.setLoginUser(new LoginUser(userId, username));
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        LoginUserHolder.clear();
    }

LoginController.java

java 复制代码
    @Operation(summary = "获取登陆用户个人信息")
    @GetMapping("info")
    public Result<SystemUserInfoVo> info() {
        Long userId = LoginUserHolder.getLoginUser().getUserId();
        SystemUserInfoVo userInfo = loginService.getLoginUserInfoById(userId);
        return Result.ok(userInfo);
    }
相关推荐
好奇龙猫2 小时前
【人工智能学习-AI入试相关题目练习-第九次】
人工智能·学习
zhangrelay2 小时前
影响移动固态磁盘稳定性的原因有哪些呢?
笔记·学习
棒棒的皮皮3 小时前
【深度学习】YOLO学习教程汇总
深度学习·学习·yolo·计算机视觉
詩不诉卿3 小时前
Zephyr学习之spi flash驱动记录(w25q128)
学习
yanyu-yaya4 小时前
速学兼复习之vue3章节3
前端·javascript·vue.js·学习·前端框架
沉默-_-4 小时前
微信小程序网络请求 wx.request 详解
网络·学习·微信小程序·小程序
嗯嗯=5 小时前
STM32单片机学习篇5
stm32·单片机·学习
头疼的程序员5 小时前
计算机网络:自顶向下方法(第七版)第二章 学习分享(二)
学习·计算机网络
沉默-_-6 小时前
微信小程序页面配置详解
学习·微信小程序·apache·微信开发者工具