接入sa-token

This commit is contained in:
none 2023-06-09 14:37:40 +08:00
parent 5005e76b81
commit c9371b0589
20 changed files with 388 additions and 295 deletions

19
pom.xml
View File

@ -138,6 +138,25 @@
<version>5.8.16</version>
</dependency>
<!-- Sa-Token 权限认证在线文档https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
<version>1.34.0</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.34.0</version>
</dependency>
<!-- Sa-Token 整合 jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
<version>1.34.0</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>

View File

@ -19,11 +19,6 @@ import xyz.playedu.api.domain.User;
import java.util.LinkedHashMap;
/**
* @Author 杭州白书科技有限公司
*
* @create 2023/3/13 09:24
*/
public class FCtx {
private static final java.lang.ThreadLocal<LinkedHashMap<String, Object>> THREAD_LOCAL =
new java.lang.ThreadLocal<>();

View File

@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.api.types;
package xyz.playedu.api.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class JwtToken {
private String token;
private Long expire;
public class AuthConfig {
@Value("${sa-token.timeout}")
private Integer expired;
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.api.config;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.stp.StpLogic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SaTokenConfigure {
// Sa-Token 整合 jwt (Simple 简单模式)
@Bean
public StpLogic getStpLogicJwt() {
return new StpLogicJwtForSimple();
}
}

View File

@ -37,11 +37,11 @@ import java.util.List;
@Slf4j
public class ExceptionController {
@ExceptionHandler(Exception.class)
public JsonResponse exceptionHandler(Exception e) {
log.error(e.getMessage());
return JsonResponse.error("系统错误", 500);
}
// @ExceptionHandler(Exception.class)
// public JsonResponse exceptionHandler(Exception e) {
// log.error(e.getMessage());
// return JsonResponse.error("系统错误", 500);
// }
@ExceptionHandler(ServiceException.class)
public JsonResponse serviceExceptionHandler(ServiceException e) {

View File

@ -23,7 +23,6 @@ import org.springframework.web.bind.annotation.*;
import xyz.playedu.api.BCtx;
import xyz.playedu.api.bus.BackendBus;
import xyz.playedu.api.constant.BPermissionConstant;
import xyz.playedu.api.constant.SystemConstant;
import xyz.playedu.api.domain.AdminUser;
import xyz.playedu.api.event.AdminUserLoginEvent;
import xyz.playedu.api.exception.JwtLogoutException;
@ -32,9 +31,8 @@ import xyz.playedu.api.middleware.ImageCaptchaCheckMiddleware;
import xyz.playedu.api.request.backend.LoginRequest;
import xyz.playedu.api.request.backend.PasswordChangeRequest;
import xyz.playedu.api.service.AdminUserService;
import xyz.playedu.api.service.JWTService;
import xyz.playedu.api.service.BackendAuthService;
import xyz.playedu.api.types.JsonResponse;
import xyz.playedu.api.types.JwtToken;
import xyz.playedu.api.util.HelperUtil;
import xyz.playedu.api.util.IpUtil;
import xyz.playedu.api.util.RequestUtil;
@ -49,7 +47,7 @@ public class LoginController {
@Autowired private BackendBus backendBus;
@Autowired private JWTService jwtService;
@Autowired private BackendAuthService authService;
@Autowired private ApplicationContext ctx;
@ -69,19 +67,16 @@ public class LoginController {
return JsonResponse.error("当前用户已禁止登录");
}
String url = RequestUtil.url();
JwtToken token =
jwtService.generate(adminUser.getId(), url, SystemConstant.JWT_PRV_ADMIN_USER);
String token = authService.loginUsingId(adminUser.getId(), RequestUtil.url());
HashMap<String, Object> data = new HashMap<>();
data.put("token", token.getToken());
data.put("expire", token.getExpire());
data.put("token", token);
ctx.publishEvent(
new AdminUserLoginEvent(
this,
adminUser.getId(),
token.getToken(),
token,
IpUtil.getIpAddress(),
adminUser.getLoginTimes()));
@ -90,7 +85,7 @@ public class LoginController {
@PostMapping("/logout")
public JsonResponse logout() throws JwtLogoutException {
jwtService.adminUserLogout(RequestUtil.token());
authService.logout();
return JsonResponse.success("success");
}

View File

@ -24,36 +24,28 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xyz.playedu.api.FCtx;
import xyz.playedu.api.constant.SystemConstant;
import xyz.playedu.api.domain.User;
import xyz.playedu.api.event.UserLoginEvent;
import xyz.playedu.api.event.UserLogoutEvent;
import xyz.playedu.api.exception.JwtLogoutException;
import xyz.playedu.api.exception.LimitException;
import xyz.playedu.api.middleware.ImageCaptchaCheckMiddleware;
import xyz.playedu.api.request.frontend.LoginPasswordRequest;
import xyz.playedu.api.service.JWTService;
import xyz.playedu.api.service.FrontendAuthService;
import xyz.playedu.api.service.UserService;
import xyz.playedu.api.types.JsonResponse;
import xyz.playedu.api.types.JwtToken;
import xyz.playedu.api.util.HelperUtil;
import xyz.playedu.api.util.IpUtil;
import xyz.playedu.api.util.RequestUtil;
import java.util.HashMap;
/**
* @Author 杭州白书科技有限公司
*
* @create 2023/3/2 21:51
*/
@RestController
@RequestMapping("/api/v1/auth/login")
public class LoginController {
@Autowired private UserService userService;
@Autowired private JWTService jwtService;
@Autowired private FrontendAuthService authService;
@Autowired private ApplicationContext ctx;
@ -74,19 +66,17 @@ public class LoginController {
return JsonResponse.error("当前学员已锁定无法登录");
}
JwtToken token =
jwtService.generate(user.getId(), RequestUtil.url(), SystemConstant.JWT_PRV_USER);
String token = authService.loginUsingId(user.getId(), RequestUtil.url());
HashMap<String, Object> data = new HashMap<>();
data.put("token", token.getToken());
data.put("expired", token.getExpire());
data.put("token", token);
ctx.publishEvent(
new UserLoginEvent(
this,
user.getId(),
user.getEmail(),
token.getToken(),
token,
IpUtil.getIpAddress(),
RequestUtil.ua()));
@ -94,8 +84,8 @@ public class LoginController {
}
@PostMapping("/logout")
public JsonResponse logout() throws JwtLogoutException {
jwtService.userLogout(RequestUtil.token());
public JsonResponse logout() {
authService.logout();
ctx.publishEvent(new UserLogoutEvent(this, FCtx.getId(), FCtx.getJwtJti()));
return JsonResponse.success();
}

View File

@ -66,8 +66,11 @@ public class UserController {
@GetMapping("/detail")
public JsonResponse detail() {
User user = FCtx.getUser();
List<Department> departments =
departmentService.listByIds(userService.getDepIdsByUserId(user.getId()));
List<Department> departments = new ArrayList<>();
List<Integer> depIds = userService.getDepIdsByUserId(user.getId());
if (depIds != null && depIds.size() > 0) {
departmentService.listByIds(depIds);
}
user.setIdCard(PrivacyUtil.hideIDCard(user.getIdCard()));

View File

@ -22,36 +22,34 @@ import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import xyz.playedu.api.constant.SystemConstant;
import xyz.playedu.api.event.UserLoginEvent;
import xyz.playedu.api.exception.JwtLogoutException;
import xyz.playedu.api.service.JWTService;
import xyz.playedu.api.service.FrontendAuthService;
import xyz.playedu.api.service.UserLoginRecordService;
import xyz.playedu.api.types.JWTPayload;
import xyz.playedu.api.util.IpUtil;
/**
* @Author 杭州白书科技有限公司
*
* @create 2023/3/10 13:45
*/
import java.util.HashMap;
@Component
@Slf4j
public class UserLoginListener {
@Autowired private UserLoginRecordService loginRecordService;
@Autowired private JWTService jwtService;
@Autowired private FrontendAuthService authService;
@Async
@EventListener
public void updateLoginInfo(UserLoginEvent event) throws JwtLogoutException {
public void updateLoginInfo(UserLoginEvent event) {
String ipArea = IpUtil.getRealAddressByIP(event.getIp());
JWTPayload payload = jwtService.parse(event.getToken(), SystemConstant.JWT_PRV_USER);
HashMap<String, String> tokenData = authService.parse(event.getToken());
String jti = tokenData.get("jti");
Long exp = Long.parseLong(tokenData.get("exp"));
loginRecordService.store(
event.getUserId(),
payload.getJti(),
payload.getExp(),
jti,
exp,
event.getIp(),
ipArea,
event.getUserAgent().getBrowser().toString(),

View File

@ -27,15 +27,12 @@ import org.springframework.web.servlet.HandlerInterceptor;
import xyz.playedu.api.BCtx;
import xyz.playedu.api.bus.AppBus;
import xyz.playedu.api.bus.BackendBus;
import xyz.playedu.api.constant.SystemConstant;
import xyz.playedu.api.domain.AdminUser;
import xyz.playedu.api.service.AdminUserService;
import xyz.playedu.api.service.AppConfigService;
import xyz.playedu.api.service.JWTService;
import xyz.playedu.api.types.JWTPayload;
import xyz.playedu.api.service.BackendAuthService;
import xyz.playedu.api.types.JsonResponse;
import xyz.playedu.api.util.HelperUtil;
import xyz.playedu.api.util.RequestUtil;
import java.io.IOException;
import java.util.Map;
@ -44,7 +41,7 @@ import java.util.Map;
@Slf4j
public class AdminMiddleware implements HandlerInterceptor {
@Autowired private JWTService jwtService;
@Autowired private BackendAuthService authService;
@Autowired private AdminUserService adminUserService;
@ -70,33 +67,23 @@ public class AdminMiddleware implements HandlerInterceptor {
return HandlerInterceptor.super.preHandle(request, response, handler);
}
String token = RequestUtil.token();
if (token.length() == 0) {
if (!authService.check()) {
return responseTransform(response, 401, "请登录");
}
try {
JWTPayload payload = jwtService.parse(token, SystemConstant.JWT_PRV_ADMIN_USER);
AdminUser adminUser = adminUserService.findById(payload.getSub());
if (adminUser == null) {
return responseTransform(response, 401, "管理员不存在");
}
if (adminUser.getIsBanLogin() == 1) {
return responseTransform(response, 403, "当前管理员禁止登录");
}
BCtx.setId(payload.getSub());
BCtx.setAdminUser(adminUser);
BCtx.setAdminPer(backendBus.adminUserPermissions(adminUser.getId()));
return HandlerInterceptor.super.preHandle(request, response, handler);
} catch (Exception e) {
if (appBus.isDev()) {
log.debug("jwt解析失败:" + e.getMessage());
}
return responseTransform(response, 401, "请重新登录");
AdminUser adminUser = adminUserService.findById(authService.userId());
if (adminUser == null) {
return responseTransform(response, 401, "管理员不存在");
}
if (adminUser.getIsBanLogin() == 1) {
return responseTransform(response, 403, "当前管理员禁止登录");
}
BCtx.setId(authService.userId());
BCtx.setAdminUser(adminUser);
BCtx.setAdminPer(backendBus.adminUserPermissions(adminUser.getId()));
return HandlerInterceptor.super.preHandle(request, response, handler);
}
private boolean responseTransform(HttpServletResponse response, int code, String msg)

View File

@ -26,27 +26,19 @@ import org.springframework.web.servlet.HandlerInterceptor;
import xyz.playedu.api.FCtx;
import xyz.playedu.api.constant.FrontendConstant;
import xyz.playedu.api.constant.SystemConstant;
import xyz.playedu.api.domain.User;
import xyz.playedu.api.service.JWTService;
import xyz.playedu.api.service.FrontendAuthService;
import xyz.playedu.api.service.UserService;
import xyz.playedu.api.types.JWTPayload;
import xyz.playedu.api.types.JsonResponse;
import xyz.playedu.api.util.HelperUtil;
import xyz.playedu.api.util.RequestUtil;
import java.io.IOException;
/**
* @Author 杭州白书科技有限公司
*
* @create 2023/3/13 09:40
*/
@Component
@Slf4j
public class FrontMiddleware implements HandlerInterceptor {
@Autowired private JWTService jwtService;
@Autowired private FrontendAuthService authService;
@Autowired private UserService userService;
@ -62,31 +54,23 @@ public class FrontMiddleware implements HandlerInterceptor {
return HandlerInterceptor.super.preHandle(request, response, handler);
}
String token = RequestUtil.token();
if (token.length() == 0) {
if (!authService.check()) {
return responseTransform(response, 401, "请登录");
}
try {
JWTPayload payload = jwtService.parse(token, SystemConstant.JWT_PRV_USER);
User user = userService.find(payload.getSub());
if (user == null) {
return responseTransform(response, 401, "请重新登录");
}
if (user.getIsLock() == 1) {
return responseTransform(response, 403, "当前学员已锁定无法登录");
}
FCtx.setUser(user);
FCtx.setId(user.getId());
FCtx.setJWtJti(payload.getJti());
return HandlerInterceptor.super.preHandle(request, response, handler);
} catch (Exception e) {
log.error(e.getMessage());
User user = userService.find(authService.userId());
if (user == null) {
return responseTransform(response, 401, "请重新登录");
}
if (user.getIsLock() == 1) {
return responseTransform(response, 403, "当前学员已锁定无法登录");
}
FCtx.setUser(user);
FCtx.setId(user.getId());
FCtx.setJWtJti(authService.jti());
return HandlerInterceptor.super.preHandle(request, response, handler);
}
private boolean responseTransform(HttpServletResponse response, int code, String msg)

View File

@ -13,34 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.api.types;
package xyz.playedu.api.service;
import lombok.Data;
import java.util.HashMap;
/**
* @see https://www.rfc-editor.org/rfc/rfc7519#section-4.1
*/
@Data
public class JWTPayload {
public interface AuthService {
String loginUsingId(Integer userId, String loginUrl, String prv);
/** subject */
private Integer sub;
boolean check(String prv);
/** Issued At */
private Long iat;
Integer userId();
/** Expiration Time */
private Long exp;
void logout();
/** Not Before */
private Long nbf;
String jti();
/** JWT ID */
private String jti;
Long expired();
/** Issuer */
private String iss;
/** Payload */
private String prv;
HashMap<String, String> parse(String token);
}

View File

@ -15,20 +15,18 @@
*/
package xyz.playedu.api.service;
import xyz.playedu.api.exception.JwtLogoutException;
import xyz.playedu.api.types.JWTPayload;
import xyz.playedu.api.types.JwtToken;
import java.util.HashMap;
public interface JWTService {
JwtToken generate(Integer userId, String iss, String prv);
public interface BackendAuthService {
String loginUsingId(Integer userId, String loginUrl);
boolean isInBlack(String jti);
boolean check();
void logout(String token, String prv) throws JwtLogoutException;
Integer userId();
void userLogout(String token) throws JwtLogoutException;
void logout();
void adminUserLogout(String token) throws JwtLogoutException;
String jti();
JWTPayload parse(String token, String prv) throws JwtLogoutException;
HashMap<String, String> parse(String token);
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.api.service;
import java.util.HashMap;
public interface FrontendAuthService {
String loginUsingId(Integer userId, String loginUrl);
boolean check();
Integer userId();
void logout();
String jti();
HashMap<String, String> parse(String token);
}

View File

@ -19,11 +19,6 @@ import com.baomidou.mybatisplus.extension.service.IService;
import xyz.playedu.api.domain.UserLoginRecord;
/**
* @author tengteng
* @description 针对表user_login_records的数据库操作Service
* @createDate 2023-03-10 13:40:33
*/
public interface UserLoginRecordService extends IService<UserLoginRecord> {
UserLoginRecord store(
Integer userId,

View File

@ -0,0 +1,87 @@
/*
* Copyright 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.api.service.impl;
import cn.dev33.satoken.stp.SaLoginConfig;
import cn.dev33.satoken.stp.StpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import xyz.playedu.api.config.AuthConfig;
import xyz.playedu.api.service.AuthService;
import java.util.HashMap;
@Service
@Slf4j
public class AuthServiceImpl implements AuthService {
@Autowired private AuthConfig authConfig;
@Override
public String loginUsingId(Integer userId, String loginUrl, String prv) {
StpUtil.login(
userId,
SaLoginConfig.setExtra("url", loginUrl)
.setExtra("prv", prv)
.setExtra(
"exp",
String.valueOf(
System.currentTimeMillis()
+ authConfig.getExpired() * 1000L)));
return StpUtil.getTokenValue();
}
@Override
public boolean check(String prv) {
if (!StpUtil.isLogin()) {
return false;
}
String tokenPrv = (String) StpUtil.getExtra("prv");
return prv.equals(tokenPrv);
}
@Override
public Integer userId() {
return StpUtil.getLoginIdAsInt();
}
@Override
public void logout() {
StpUtil.logout();
}
@Override
public String jti() {
return (String) StpUtil.getExtra("rnStr");
}
@Override
public Long expired() {
return (Long) StpUtil.getExtra("exp");
}
@Override
public HashMap<String, String> parse(String token) {
HashMap<String, String> data = new HashMap<>();
data.put("jti", (String) StpUtil.getExtra(token, "rnStr"));
data.put("exp", (String) StpUtil.getExtra(token, "exp"));
return data;
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.api.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import xyz.playedu.api.constant.SystemConstant;
import xyz.playedu.api.service.AuthService;
import xyz.playedu.api.service.BackendAuthService;
import java.util.HashMap;
@Service
public class BackendAuthServiceImpl implements BackendAuthService {
@Autowired private AuthService authService;
@Override
public String loginUsingId(Integer userId, String loginUrl) {
return authService.loginUsingId(userId, loginUrl, SystemConstant.JWT_PRV_ADMIN_USER);
}
@Override
public boolean check() {
return authService.check(SystemConstant.JWT_PRV_ADMIN_USER);
}
@Override
public Integer userId() {
return authService.userId();
}
@Override
public void logout() {
authService.logout();
}
@Override
public String jti() {
return authService.jti();
}
@Override
public HashMap<String, String> parse(String token) {
return authService.parse(token);
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.api.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import xyz.playedu.api.constant.SystemConstant;
import xyz.playedu.api.service.AuthService;
import xyz.playedu.api.service.FrontendAuthService;
import java.util.HashMap;
@Service
public class FrontendAuthServiceImpl implements FrontendAuthService {
@Autowired private AuthService authService;
@Override
public String loginUsingId(Integer userId, String loginUrl) {
return authService.loginUsingId(userId, loginUrl, SystemConstant.JWT_PRV_USER);
}
@Override
public boolean check() {
return authService.check(SystemConstant.JWT_PRV_USER);
}
@Override
public Integer userId() {
return authService.userId();
}
@Override
public void logout() {
authService.logout();
}
@Override
public String jti() {
return authService.jti();
}
@Override
public HashMap<String, String> parse(String token) {
return authService.parse(token);
}
}

View File

@ -1,140 +0,0 @@
/*
* Copyright 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.api.service.impl;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import xyz.playedu.api.constant.SystemConstant;
import xyz.playedu.api.exception.JwtLogoutException;
import xyz.playedu.api.service.JWTService;
import xyz.playedu.api.types.JWTPayload;
import xyz.playedu.api.types.JwtToken;
import xyz.playedu.api.util.HelperUtil;
import xyz.playedu.api.util.RedisUtil;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import javax.crypto.SecretKey;
@Slf4j
@Service
public class JwtServiceImpl implements JWTService {
@Value("${playedu.jwt.key}")
private String ConfigKey;
@Value("${playedu.jwt.expire}")
private Long ConfigExpire;
@Value("${playedu.jwt.cache-black-prefix}")
private String ConfigCacheBlackPrefix;
public JwtToken generate(Integer userId, String iss, String prv) {
long curTime = System.currentTimeMillis();
JWTPayload payload = new JWTPayload();
payload.setPrv(prv);
payload.setIss(iss);
payload.setJti(HelperUtil.uuid());
payload.setNbf(curTime);
payload.setIat(curTime);
payload.setExp(curTime + ConfigExpire * 1000);
payload.setSub(userId);
JwtBuilder builder = Jwts.builder();
builder.setId(payload.getJti())
.setIssuedAt(new Date(payload.getIat()))
.claim("prv", payload.getPrv());
builder.setExpiration(new Date(payload.getExp())).setIssuer(payload.getIss());
builder.setSubject(String.valueOf(payload.getSub()))
.setNotBefore(new Date(payload.getNbf()));
builder.signWith(getSecretKey());
JwtToken token = new JwtToken();
token.setToken(builder.compact());
token.setExpire(payload.getExp() / 1000);
return token;
}
public JWTPayload parse(String token, String prv) throws JwtLogoutException {
Claims claims = parseToken(token, prv);
JWTPayload payload = new JWTPayload();
payload.setSub(Integer.valueOf(claims.getSubject()));
payload.setIss(claims.getIssuer());
payload.setPrv((String) claims.get("prv"));
payload.setNbf(claims.getNotBefore().getTime());
payload.setExp(claims.getExpiration().getTime());
payload.setIat(claims.getIssuedAt().getTime());
payload.setJti(claims.getId());
return payload;
}
public boolean isInBlack(String jti) {
return RedisUtil.exists(getBlackCacheKey(jti));
}
public void logout(String token, String prv) throws JwtLogoutException {
Claims claims = parseToken(token, prv);
String cacheKey = getBlackCacheKey(claims.getId());
Long expire = (claims.getExpiration().getTime() - System.currentTimeMillis()) / 1000;
RedisUtil.set(cacheKey, 1, expire);
}
@Override
public void userLogout(String token) throws JwtLogoutException {
logout(token, SystemConstant.JWT_PRV_USER);
}
@Override
public void adminUserLogout(String token) throws JwtLogoutException {
logout(token, SystemConstant.JWT_PRV_ADMIN_USER);
}
private Claims parseToken(String token, String prv) throws JwtLogoutException {
Claims claims =
(Claims)
Jwts.parserBuilder()
.setSigningKey(getSecretKey())
.require("prv", prv)
.build()
.parse(token)
.getBody();
if (isInBlack(claims.getId())) {
throw new JwtLogoutException();
}
return claims;
}
private SecretKey getSecretKey() {
return Keys.hmacShaKeyFor(ConfigKey.getBytes(StandardCharsets.UTF_8));
}
private String getBlackCacheKey(String jti) {
return ConfigCacheBlackPrefix + jti;
}
}

View File

@ -56,6 +56,14 @@ mybatis-plus:
# configuration:
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
sa-token:
token-name: "Authorization"
timeout: 1296000 #token有效期[单位:秒,默认15天]
is-concurrent: false #限制同时登录
is-share: false
jwt-secret-key: "playeduxyz"
token-prefix: "Bearer"
# PlayEdu
playedu:
# 图形验证码