diff --git a/pom.xml b/pom.xml index 61e8723..4109ed8 100644 --- a/pom.xml +++ b/pom.xml @@ -138,6 +138,25 @@ 5.8.16 + + + cn.dev33 + sa-token-spring-boot3-starter + 1.34.0 + + + + cn.dev33 + sa-token-dao-redis-jackson + 1.34.0 + + + + cn.dev33 + sa-token-jwt + 1.34.0 + + org.springdoc springdoc-openapi-starter-webmvc-ui diff --git a/src/main/java/xyz/playedu/api/FCtx.java b/src/main/java/xyz/playedu/api/FCtx.java index 4b6ebb1..56ae4d3 100644 --- a/src/main/java/xyz/playedu/api/FCtx.java +++ b/src/main/java/xyz/playedu/api/FCtx.java @@ -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> THREAD_LOCAL = new java.lang.ThreadLocal<>(); diff --git a/src/main/java/xyz/playedu/api/types/JwtToken.java b/src/main/java/xyz/playedu/api/config/AuthConfig.java similarity index 71% rename from src/main/java/xyz/playedu/api/types/JwtToken.java rename to src/main/java/xyz/playedu/api/config/AuthConfig.java index 5c1ca87..d793b75 100644 --- a/src/main/java/xyz/playedu/api/types/JwtToken.java +++ b/src/main/java/xyz/playedu/api/config/AuthConfig.java @@ -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; } diff --git a/src/main/java/xyz/playedu/api/config/SaTokenConfigure.java b/src/main/java/xyz/playedu/api/config/SaTokenConfigure.java new file mode 100644 index 0000000..02c8ec2 --- /dev/null +++ b/src/main/java/xyz/playedu/api/config/SaTokenConfigure.java @@ -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(); + } +} diff --git a/src/main/java/xyz/playedu/api/controller/ExceptionController.java b/src/main/java/xyz/playedu/api/controller/ExceptionController.java index 5bc268b..6358dd2 100644 --- a/src/main/java/xyz/playedu/api/controller/ExceptionController.java +++ b/src/main/java/xyz/playedu/api/controller/ExceptionController.java @@ -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) { diff --git a/src/main/java/xyz/playedu/api/controller/backend/LoginController.java b/src/main/java/xyz/playedu/api/controller/backend/LoginController.java index 119b177..bb42749 100644 --- a/src/main/java/xyz/playedu/api/controller/backend/LoginController.java +++ b/src/main/java/xyz/playedu/api/controller/backend/LoginController.java @@ -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 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"); } diff --git a/src/main/java/xyz/playedu/api/controller/frontend/LoginController.java b/src/main/java/xyz/playedu/api/controller/frontend/LoginController.java index b5ad7ea..b33f2d1 100644 --- a/src/main/java/xyz/playedu/api/controller/frontend/LoginController.java +++ b/src/main/java/xyz/playedu/api/controller/frontend/LoginController.java @@ -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 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(); } diff --git a/src/main/java/xyz/playedu/api/controller/frontend/UserController.java b/src/main/java/xyz/playedu/api/controller/frontend/UserController.java index 00c5876..9091b97 100644 --- a/src/main/java/xyz/playedu/api/controller/frontend/UserController.java +++ b/src/main/java/xyz/playedu/api/controller/frontend/UserController.java @@ -66,8 +66,11 @@ public class UserController { @GetMapping("/detail") public JsonResponse detail() { User user = FCtx.getUser(); - List departments = - departmentService.listByIds(userService.getDepIdsByUserId(user.getId())); + List departments = new ArrayList<>(); + List depIds = userService.getDepIdsByUserId(user.getId()); + if (depIds != null && depIds.size() > 0) { + departmentService.listByIds(depIds); + } user.setIdCard(PrivacyUtil.hideIDCard(user.getIdCard())); diff --git a/src/main/java/xyz/playedu/api/listener/UserLoginListener.java b/src/main/java/xyz/playedu/api/listener/UserLoginListener.java index 7eb0823..fdfce20 100644 --- a/src/main/java/xyz/playedu/api/listener/UserLoginListener.java +++ b/src/main/java/xyz/playedu/api/listener/UserLoginListener.java @@ -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 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(), diff --git a/src/main/java/xyz/playedu/api/middleware/AdminMiddleware.java b/src/main/java/xyz/playedu/api/middleware/AdminMiddleware.java index f7f9e3e..298e30a 100644 --- a/src/main/java/xyz/playedu/api/middleware/AdminMiddleware.java +++ b/src/main/java/xyz/playedu/api/middleware/AdminMiddleware.java @@ -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) diff --git a/src/main/java/xyz/playedu/api/middleware/FrontMiddleware.java b/src/main/java/xyz/playedu/api/middleware/FrontMiddleware.java index 8b6248f..2ad5da5 100644 --- a/src/main/java/xyz/playedu/api/middleware/FrontMiddleware.java +++ b/src/main/java/xyz/playedu/api/middleware/FrontMiddleware.java @@ -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) diff --git a/src/main/java/xyz/playedu/api/types/JWTPayload.java b/src/main/java/xyz/playedu/api/service/AuthService.java similarity index 57% rename from src/main/java/xyz/playedu/api/types/JWTPayload.java rename to src/main/java/xyz/playedu/api/service/AuthService.java index 4b8f262..385bf67 100644 --- a/src/main/java/xyz/playedu/api/types/JWTPayload.java +++ b/src/main/java/xyz/playedu/api/service/AuthService.java @@ -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 parse(String token); } diff --git a/src/main/java/xyz/playedu/api/service/JWTService.java b/src/main/java/xyz/playedu/api/service/BackendAuthService.java similarity index 55% rename from src/main/java/xyz/playedu/api/service/JWTService.java rename to src/main/java/xyz/playedu/api/service/BackendAuthService.java index 5290732..2d1c5f6 100644 --- a/src/main/java/xyz/playedu/api/service/JWTService.java +++ b/src/main/java/xyz/playedu/api/service/BackendAuthService.java @@ -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 parse(String token); } diff --git a/src/main/java/xyz/playedu/api/service/FrontendAuthService.java b/src/main/java/xyz/playedu/api/service/FrontendAuthService.java new file mode 100644 index 0000000..b9d641a --- /dev/null +++ b/src/main/java/xyz/playedu/api/service/FrontendAuthService.java @@ -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 parse(String token); +} diff --git a/src/main/java/xyz/playedu/api/service/UserLoginRecordService.java b/src/main/java/xyz/playedu/api/service/UserLoginRecordService.java index 0b94bfd..01ef9be 100644 --- a/src/main/java/xyz/playedu/api/service/UserLoginRecordService.java +++ b/src/main/java/xyz/playedu/api/service/UserLoginRecordService.java @@ -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 store( Integer userId, diff --git a/src/main/java/xyz/playedu/api/service/impl/AuthServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/AuthServiceImpl.java new file mode 100644 index 0000000..cb32e54 --- /dev/null +++ b/src/main/java/xyz/playedu/api/service/impl/AuthServiceImpl.java @@ -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 parse(String token) { + HashMap data = new HashMap<>(); + data.put("jti", (String) StpUtil.getExtra(token, "rnStr")); + data.put("exp", (String) StpUtil.getExtra(token, "exp")); + return data; + } +} diff --git a/src/main/java/xyz/playedu/api/service/impl/BackendAuthServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/BackendAuthServiceImpl.java new file mode 100644 index 0000000..62e569c --- /dev/null +++ b/src/main/java/xyz/playedu/api/service/impl/BackendAuthServiceImpl.java @@ -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 parse(String token) { + return authService.parse(token); + } +} diff --git a/src/main/java/xyz/playedu/api/service/impl/FrontendAuthServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/FrontendAuthServiceImpl.java new file mode 100644 index 0000000..ecc0743 --- /dev/null +++ b/src/main/java/xyz/playedu/api/service/impl/FrontendAuthServiceImpl.java @@ -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 parse(String token) { + return authService.parse(token); + } +} diff --git a/src/main/java/xyz/playedu/api/service/impl/JwtServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/JwtServiceImpl.java deleted file mode 100644 index 2a6cf09..0000000 --- a/src/main/java/xyz/playedu/api/service/impl/JwtServiceImpl.java +++ /dev/null @@ -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; - } -} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2d059e9..1d5e3a2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -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: # 图形验证码