SpringBoot+Jwt实现登录和Token验证
技术框架
名称 | 描述 |
---|---|
SpringBoot | 基础工程 |
MybatisPlus | ORM框架 |
Knife4j & Swagger-ui | API文档 |
JWT | token生成和验证 |
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;
}
}