SpringBoot+Jwt实现登录和Token验证

SpringBoot+Jwt实现登录和Token验证

技术框架

名称描述
SpringBoot基础工程
MybatisPlusORM框架
Knife4j & Swagger-uiAPI文档
JWTtoken生成和验证

1.表结构

DROP TABLE IF EXISTS `T_SYS_USER`;
CREATE TABLE `T_SYS_USER`
(
    `id`              bigint(20)   NOT NULL,
    `account`         varchar(32)  NOT NULL,
    `username`        varchar(32)  NOT NULL,
    `password`        varchar(100) NOT NULL,
    `status`          varchar(10)  NOT NULL,
    `type`            smallint(6)  NOT NULL DEFAULT '0',
    `create_by`       bigint(20)   NOT NULL,
    `last_login_time` TIMESTAMP,
    `create_time`     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `update_by`       bigint(20)   NOT NULL,
    `update_time`     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `deleted`         smallint(6)  NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`),
    UNIQUE KEY `IDX_T_SYS_USER_ACCOUNT_USERNAME` (`account`, `username`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

2.实体类

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_sys_user")
public class SysUser implements Serializable {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String account;
    private String username;
    private String password;
    private String status;
    private Integer type;
    private LocalDateTime lastLoginTime;
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @TableLogic(delval = "id", value = "0")
    @TableField("deleted")
    private Integer deleted;
}

3.service服务类

public interface LoginService {

    LoginUser login(LoginBO loginBO);

}

4.service实现类

@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class LoginServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements LoginService {

    private final RedisUtil redisUtil;

    @Override
    public LoginUser login(LoginBO loginBO) {
        SysUser user = this.lambdaQuery().eq(SysUser::getUsername, loginBO.getUsername()).one();
        if (Objects.isNull(user)) {
            throw BusinessException.build("用户名 : %s 不存在。", loginBO.getUsername());
        }
        if (!BCryptUtils.matches(loginBO.getPassword(), user.getPassword())) {
            throw BusinessException.build("验证失败 :用户名或密码有误。");
        }
        user.setLastLoginTime(LocalDateTime.now());
        this.updateById(user);
        LoginUserDTO loginUser =  BeanUtil.copyProperties(user, LoginUserDTO.class);

        long expire = ExpireTimeEnum.ONE_DAY.getTime() / 2L;
        redisUtil.set(Constants.USER_ID + ":" + loginUser.getId(), loginUser, expire);

        Map<String, Object> map = new HashMap<>();
        map.put(Constants.USER_ID, loginUser.getId());
        String token = JwtUtil.createJwt(map, expire * 1000L);
        return LoginUser.builder().user(loginUser).token(token).build();
    }
}

5.前端控制类

@Slf4j
@Tag(name = "登录", description = "登录")
@RestController
@RequestMapping("rbac")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class LoginController {

    private final LoginService loginService;
    @Operation(operationId = "登录",description = "登录")
    @PostMapping("/login")
    public Result<LoginUser> login(@RequestBody LoginBO loginBO) {
        return Result.success(loginService.login(loginBO));
    }
}

6.Token校验

public class TokenFilter implements Filter, Ordered {

    private static final String TOKEN = "token";

    private static final String TOKEN_EXPIRED = "500";
    private static final String TOKEN_CODE = "302";
    private List<String> whitelist;
    private HandlerExceptionResolver handlerExceptionResolver;

    public void setHandlerExceptionResolver(HandlerExceptionResolver handlerExceptionResolver) {
        this.handlerExceptionResolver = handlerExceptionResolver;
    }

    public void setWhitelist(List<String> whitelist) {
        this.whitelist = whitelist;
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = ( HttpServletRequest ) req;
        HttpServletResponse response = ( HttpServletResponse ) res;

        String contextPath = request.getContextPath();
        String uri = request.getRequestURI().replace(contextPath, "");
        if (Objects.nonNull(whitelist) && (whitelist.contains(uri) || regexMatch(uri))) {
            chain.doFilter(request, response);
            return;
        }

        String token = request.getHeader(TOKEN);
        if (StringUtils.isBlank(token)) {
            token = request.getParameter(TOKEN);
        }

        if (Objects.isNull(token) || StringUtils.isEmpty(token.trim())) {
            this.handlerExceptionResolver.resolveException(request, response, null, BusinessException.buildWithCode(TOKEN_CODE, "token不能为空。"));
            return;
        }

        try {
            Map<String, String> map = JwtUtil.verifyJwt(token);
            if (map.containsKey(Constants.USER_ID)) {
                map.forEach(request::setAttribute);
                chain.doFilter(request, response);
            } else {
                this.handlerExceptionResolver.resolveException(request, response, null, BusinessException.buildWithCode(TOKEN_CODE, "token无效。"));
            }
        } catch (TokenExpiredException e) {
            this.handlerExceptionResolver.resolveException(request, response, null, BusinessException.buildWithCode(TOKEN_EXPIRED, "token已过期。"));
        } catch (JWTVerificationException e) {
            this.handlerExceptionResolver.resolveException(request, response, null, BusinessException.buildWithCode(TOKEN_CODE, "token校验失败。"));
        }
    }

    private boolean regexMatch(String uri) {
        return this.whitelist.stream().anyMatch(s -> Pattern.compile(s).matcher(uri).matches());
    }

    @Override
    public int getOrder() {
        return Integer.MIN_VALUE;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值