From c79d96e14b1ced37252ae3d211f1b656026ca71f Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Tue, 25 Nov 2025 09:39:25 +0800 Subject: [PATCH] =?UTF-8?q?init=EF=BC=9A=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 33 ++ pom.xml | 264 +++++++++++++ .../com/ngskcloud/BpmsSsoApplication.java | 30 ++ .../advise/GlobalExceptionHandler.java | 15 + .../com/ngskcloud/config/FeignConfig.java | 24 ++ .../com/ngskcloud/config/MybatisConfig.java | 9 + .../com/ngskcloud/config/SaTokenConfig.java | 17 + .../ngskcloud/constant/SystemConstant.java | 20 + .../controller/SsoServerController.java | 348 ++++++++++++++++++ .../java/com/ngskcloud/entity/AdminUser.java | 174 +++++++++ src/main/java/com/ngskcloud/entity/User.java | 266 +++++++++++++ .../com/ngskcloud/feign/SsoServerClient.java | 46 +++ .../interceptor/SaTokenInterceptorConfig.java | 18 + .../com/ngskcloud/mapper/AdminUserMapper.java | 10 + .../com/ngskcloud/mapper/AdminUserMapper.xml | 28 ++ .../java/com/ngskcloud/mapper/UserMapper.java | 10 + .../java/com/ngskcloud/mapper/UserMapper.xml | 7 + .../ngskcloud/service/AdminUserService.java | 12 + .../service/Impl/AdminUserServiceImpl.java | 20 + .../service/Impl/UserServiceImpl.java | 22 ++ .../com/ngskcloud/service/UserService.java | 12 + .../java/com/ngskcloud/utils/DigestUtil.java | 62 ++++ .../java/com/ngskcloud/utils/JwtUtil.java | 222 +++++++++++ .../java/com/ngskcloud/utils/RequestUtil.java | 106 ++++++ src/main/resources/application.yml | 112 ++++++ src/main/resources/static/index.html | 6 + .../ngskcloud/BpmsSsoApplicationTests.java | 13 + 27 files changed, 1906 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/com/ngskcloud/BpmsSsoApplication.java create mode 100644 src/main/java/com/ngskcloud/advise/GlobalExceptionHandler.java create mode 100644 src/main/java/com/ngskcloud/config/FeignConfig.java create mode 100644 src/main/java/com/ngskcloud/config/MybatisConfig.java create mode 100644 src/main/java/com/ngskcloud/config/SaTokenConfig.java create mode 100644 src/main/java/com/ngskcloud/constant/SystemConstant.java create mode 100644 src/main/java/com/ngskcloud/controller/SsoServerController.java create mode 100644 src/main/java/com/ngskcloud/entity/AdminUser.java create mode 100644 src/main/java/com/ngskcloud/entity/User.java create mode 100644 src/main/java/com/ngskcloud/feign/SsoServerClient.java create mode 100644 src/main/java/com/ngskcloud/interceptor/SaTokenInterceptorConfig.java create mode 100644 src/main/java/com/ngskcloud/mapper/AdminUserMapper.java create mode 100644 src/main/java/com/ngskcloud/mapper/AdminUserMapper.xml create mode 100644 src/main/java/com/ngskcloud/mapper/UserMapper.java create mode 100644 src/main/java/com/ngskcloud/mapper/UserMapper.xml create mode 100644 src/main/java/com/ngskcloud/service/AdminUserService.java create mode 100644 src/main/java/com/ngskcloud/service/Impl/AdminUserServiceImpl.java create mode 100644 src/main/java/com/ngskcloud/service/Impl/UserServiceImpl.java create mode 100644 src/main/java/com/ngskcloud/service/UserService.java create mode 100644 src/main/java/com/ngskcloud/utils/DigestUtil.java create mode 100644 src/main/java/com/ngskcloud/utils/JwtUtil.java create mode 100644 src/main/java/com/ngskcloud/utils/RequestUtil.java create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/static/index.html create mode 100644 src/test/java/com/ngskcloud/BpmsSsoApplicationTests.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1f5f934 --- /dev/null +++ b/pom.xml @@ -0,0 +1,264 @@ + + + 4.0.0 + com.ngskcloud + bpms-sso + 0.0.1-SNAPSHOT + bpms-sso + bpms-sso + + 21 + UTF-8 + UTF-8 + 3.2.3 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + 4.1.3 + + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + io.github.openfeign + feign-jackson + 12.1 + + + + + + + + + + cn.dev33 + sa-token-spring-boot3-starter + 1.44.0 + + + + + cn.dev33 + sa-token-sso + 1.44.0 + + + + + cn.dev33 + sa-token-redis-template + 1.44.0 + + + org.apache.commons + commons-pool2 + + + + + cn.dev33 + sa-token-forest + 1.44.0 + + + + cn.dev33 + sa-token-jwt + 1.44.0 + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + 2023.0.3.3 + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + 2023.0.3.3 + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + mysql + mysql-connector-java + 8.0.27 + + + + + io.jsonwebtoken + jjwt + 0.12.6 + + + + + com.baomidou + mybatis-plus-boot-starter + 3.5.5 + + + org.mybatis + mybatis-spring + 3.0.5 + + + + com.baomidou + mybatis-plus-annotation + 3.5.5 + + + + + + + + + org.projectlombok + lombok + provided + + + + cn.hutool + hutool-json + 5.8.39 + + + cn.hutool + hutool-http + 5.8.39 + + + cn.hutool + hutool-core + 5.8.39 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 21 + 21 + UTF-8 + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + com.ngskcloud.BpmsSsoApplication + true + + + + repackage + + repackage + + + + + + + + diff --git a/src/main/java/com/ngskcloud/BpmsSsoApplication.java b/src/main/java/com/ngskcloud/BpmsSsoApplication.java new file mode 100644 index 0000000..db4595c --- /dev/null +++ b/src/main/java/com/ngskcloud/BpmsSsoApplication.java @@ -0,0 +1,30 @@ +package com.ngskcloud; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.sso.SaSsoManager; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.Environment; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +@SpringBootApplication +@MapperScan("com.ngskcloud.mapper") // 添加在这里 +public class BpmsSsoApplication { + + public static void main(String[] args) throws UnknownHostException { + SpringApplication.run(BpmsSsoApplication.class, args); + + System.out.println(); + System.out.println("---------------------- Sa-Token SSO 统一认证中心启动成功 ----------------------"); + System.out.println("配置信息:" + SaSsoManager.getServerConfig()); + System.out.println("统一认证登录地址:http://sa-sso-server.com:9000/sso/auth"); + System.out.println("测试前需要根据官网文档修改 hosts 文件,测试账号密码:sa / 123456"); + System.out.println(); + + } + +} diff --git a/src/main/java/com/ngskcloud/advise/GlobalExceptionHandler.java b/src/main/java/com/ngskcloud/advise/GlobalExceptionHandler.java new file mode 100644 index 0000000..ce17617 --- /dev/null +++ b/src/main/java/com/ngskcloud/advise/GlobalExceptionHandler.java @@ -0,0 +1,15 @@ +package com.ngskcloud.advise; + +import cn.dev33.satoken.util.SaResult; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + // 全局异常拦截 + @ExceptionHandler + public SaResult handlerException(Exception e) { + e.printStackTrace(); + return SaResult.error(e.getMessage()); + } +} diff --git a/src/main/java/com/ngskcloud/config/FeignConfig.java b/src/main/java/com/ngskcloud/config/FeignConfig.java new file mode 100644 index 0000000..4e9dcce --- /dev/null +++ b/src/main/java/com/ngskcloud/config/FeignConfig.java @@ -0,0 +1,24 @@ +package com.ngskcloud.config; + + +import feign.codec.Decoder; +import feign.codec.Encoder; + +import feign.jackson.JacksonDecoder; +import feign.jackson.JacksonEncoder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class FeignConfig { + + @Bean + public Encoder feignEncoder() { + return new JacksonEncoder(); + } + + @Bean + public Decoder feignDecoder() { + return new JacksonDecoder(); + } +} diff --git a/src/main/java/com/ngskcloud/config/MybatisConfig.java b/src/main/java/com/ngskcloud/config/MybatisConfig.java new file mode 100644 index 0000000..163a095 --- /dev/null +++ b/src/main/java/com/ngskcloud/config/MybatisConfig.java @@ -0,0 +1,9 @@ +package com.ngskcloud.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + +//@Configuration +//@MapperScan("com.ngskcloud.mapper") +//public class MybatisConfig { +//} \ No newline at end of file diff --git a/src/main/java/com/ngskcloud/config/SaTokenConfig.java b/src/main/java/com/ngskcloud/config/SaTokenConfig.java new file mode 100644 index 0000000..0aa9e4d --- /dev/null +++ b/src/main/java/com/ngskcloud/config/SaTokenConfig.java @@ -0,0 +1,17 @@ +package com.ngskcloud.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 SaTokenConfig { + + // Sa-Token 整合 jwt (Simple 简单模式) + @Bean + public StpLogic getStpLogicJwt() { + return new StpLogicJwtForSimple(); + } +} diff --git a/src/main/java/com/ngskcloud/constant/SystemConstant.java b/src/main/java/com/ngskcloud/constant/SystemConstant.java new file mode 100644 index 0000000..07efdda --- /dev/null +++ b/src/main/java/com/ngskcloud/constant/SystemConstant.java @@ -0,0 +1,20 @@ +package com.ngskcloud.constant; + +public class SystemConstant { + + public static final String VERSION = "v1.0-beta1"; + + public static final String ENV_PROD = "prod"; + + public static final String REDIS_PREFIX = "playedu:"; + + public static final String JWT_PRV_ADMIN_USER = + "dc14511e97e7eb725fb2976bc939b375"; // AdminUser的md5加密 + public static final String JWT_PRV_USER = "8f9bfe9d1345237cb3b2b205864da075"; // User的md5加密 + + public static final String INTERNAL_IP = "127.0.0.1"; + + public static final String INTERNAL_IP_AREA = "内网"; + + public static final String CONFIG_MASK = "********"; +} diff --git a/src/main/java/com/ngskcloud/controller/SsoServerController.java b/src/main/java/com/ngskcloud/controller/SsoServerController.java new file mode 100644 index 0000000..b7eb4b5 --- /dev/null +++ b/src/main/java/com/ngskcloud/controller/SsoServerController.java @@ -0,0 +1,348 @@ +package com.ngskcloud.controller; + + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.sso.model.TicketModel; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.sso.processor.SaSsoServerProcessor; +import cn.dev33.satoken.sso.template.SaSsoClientTemplate; +import cn.dev33.satoken.sso.template.SaSsoServerTemplate; +import cn.dev33.satoken.sso.template.SaSsoServerUtil; +import cn.dev33.satoken.stp.SaLoginConfig; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; +import com.ngskcloud.constant.SystemConstant; +import com.ngskcloud.entity.AdminUser; +import com.ngskcloud.entity.User; +import com.ngskcloud.service.AdminUserService; +import com.ngskcloud.service.UserService; +import com.ngskcloud.utils.DigestUtil; +import com.ngskcloud.utils.JwtUtil; +import com.ngskcloud.utils.RequestUtil; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import java.util.*; + +/** + * SSO服务端控制器 + */ +@RestController +@RequestMapping +@RequiredArgsConstructor +public class SsoServerController { + + @Value("${sa-token.timeout}") + private Integer expired; + + @Value("${sa-token.sso-server.clients.sso-client2.client}") + private String clientName; + + + @Autowired + private JwtUtil jwtUtil; + + private final UserService userService; + + private final AdminUserService adminUserService; + +// private final RedisTemplate redisTemplate; + +// /** +// * 统一认证入口 - 处理未明确指定的SSO请求 +// */ +// @RequestMapping("/auth") +// public Object auth(HttpServletRequest request) { +// return SaSsoServerProcessor.instance.dister(); +// } +// + /** + * 登录接口 + */ + @GetMapping("/sso/doLogin") + public SaResult doLogin(@RequestParam("username") String username, @RequestParam("pwd") String pwd,@RequestParam("identity") Integer identity) { + // 根据identity判断登录类型 + if (identity == null ){ + return SaResult.error("identity字段不能为空"); + }else if (1==identity) { + return getUserSaResult(username, pwd,identity); + } else if (2==identity) { + return getAdminSaResult(username, pwd,identity); + } else { + return SaResult.error("identity字段仅能为1-用户或者2-admin"); + } + + } + + private SaResult getUserSaResult(String account, String pwd,Integer identity) { + + User user = userService.find(account); + if (user == null) { + return SaResult.error("账号或密码错误"); + } + + + if (!DigestUtil.encrypt(pwd).equals(user.getPassword())) { + return SaResult.error("账号或密码错误"); + } + + if (user.getIsLock() == 1) { + return SaResult.error("当前学员已锁定无法登录"); + } + + StpUtil.login( + user.getId(), + SaLoginConfig.setExtra("url", RequestUtil.url()) + .setExtra("user_name",user.getName()) + .setExtra("account",user.getAccount()) + .setExtra("user_id",user.getId()) + .setExtra("avatar",user.getAvatar()) + .setExtra("jti", UUID.randomUUID().toString()) + .setExtra("prv", SystemConstant.JWT_PRV_USER) + .setExtra("tenant_id", "000000") + .setExtra( + "exp", + String.valueOf( + System.currentTimeMillis() / 1000 + + expired))); + + + + + String ticketAndSave = SaSsoServerUtil.createTicketAndSave(clientName, user.getId(), StpUtil.getTokenValue()); + + + return SaResult.ok("登录成功!").setData(ticketAndSave); + } + + private SaResult getAdminSaResult(String account, String pwd,Integer identity) { + AdminUser adminUser = adminUserService.findByAccount(account); + if (adminUser == null) { + return SaResult.error("账号密码错误!"); + } + + String password = DigestUtil.encrypt(pwd); + + if (!adminUser.getPassword().equals(password)) { + return SaResult.error("账号或密码错误"); + } + + + if (adminUser.getIsBanLogin().equals(1)) { + return SaResult.error("当前管理员已禁止登录"); + } + +// StpUtil.login(adminUser.getId()); + StpUtil.login( + adminUser.getId(), + SaLoginConfig.setExtra("url", RequestUtil.url()) + + .setExtra("prv", SystemConstant.JWT_PRV_ADMIN_USER) + .setExtra("identity", identity) + + .setExtra( + "exp", + String.valueOf( + System.currentTimeMillis() / 1000 + +expired))); + + + String ticketAndSave = SaSsoServerUtil.createTicketAndSave(clientName, adminUser.getId(), StpUtil.getTokenValue()); + + return SaResult.ok("登录成功!").setData(ticketAndSave); + } + + + // 注销 + @PostMapping("/sso/logout") + public SaResult logout() { + try { + StpUtil.logout(); + return SaResult.ok(); + }catch (Exception e){ + throw new RuntimeException("登出错误"); + } + } + + + // 验证ticket是否有效 + @GetMapping("/sso/checkTicket") + public SaResult checkTicket(@RequestParam(value = "ticket") String ticket,@RequestParam(value = "identity") Integer identity) { + //验证票据 +// TicketModel ticketModel = SaSsoServerUtil.checkTicketParamAndDelete(ticket); + TicketModel ticketModel = SaSsoServerUtil.checkTicket(ticket); + if (ticketModel != null) { + // 将当前会话设置为ticket对应的登录状态 + StpUtil.setTokenValue(ticketModel.getTokenValue()); + } + // StpUtil.isLogin() 为 true + boolean currentIsLogin = StpUtil.isLogin(); + + + Map map= new HashMap<>(); + + if (1==identity){ + User user = userService.find((Long)ticketModel.getLoginId()); + map.put("token",ticketModel.getTokenValue()); + map.put("id",user.getId()); + map.put("ticketModel",ticketModel); + map.put("account",user.getAccount()); + + + }else if(2==identity){ + AdminUser adminUser = adminUserService.findById((Long)ticketModel.getLoginId()); + map.put("token",ticketModel.getTokenValue()); + map.put("id",adminUser.getId()); + map.put("loginTimes",adminUser.getLoginTimes()); + map.put("ticketModel",ticketModel); + } + return SaResult.ok("验证成功!").setData(map); + } + +// // 会话是否登录 +// @GetMapping("/sso/isLogin") +// public SaResult isLogin(@RequestParam(value = "token", required = false) String token) { +//// StpUtil.getTokenValue(); +// +//// if (token != null && !token.trim().isEmpty()) { +//// StpUtil.setTokenValue(token); +//// } +//// return SaResult.ok("验证成功!").setData(StpUtil.isLogin()); +// +// try { +// // 1. 参数校验 +// if (token == null || token.trim().isEmpty()) { +// return SaResult.error("token不能为空").setData(400); +// } +//// +//// if (identity == null) { +//// return SaResult.error("identity身份类型不能为空").setCode(400); +//// } +//// +//// if (identity != 1 && identity != 2) { +//// return SaResult.error("identity只能为1(用户)或2(管理员)").setCode(400); +//// } +//// +// // 2. 设置当前token上下文 +// StpUtil.setTokenValue(token); +// +// // 3. 基础登录状态验证 +// if (!StpUtil.isLogin()) { +// return SaResult.error("token无效或已过期").setCode(401); +// } +// +// // 4. 获取登录ID并验证身份一致性 +// Object loginId = StpUtil.getLoginIdDefaultNull(); +// if (loginId == null) { +// return SaResult.error("无法获取用户信息").setCode(401); +// } +// +// // 5. 验证token活跃状态(防止token被踢出) +// if (!StpUtil.isLogin(loginId)) { +// return SaResult.error("token已被踢出或失效").setCode(401); +// } +// +// +// // 6. 根据身份类型进行额外验证 +// Integer identity = (Integer) StpUtil.getExtra("identity",""); +// +// boolean isValid = validateUserIdentity(loginId,identity,token); +// if (!isValid) { +// return SaResult.error("身份类型与token不匹配").setCode(403); +// } +// +// // 7. 获取token剩余有效期 +// long timeout = StpUtil.getTokenTimeout(); +// +//// // 8. 返回详细的验证结果 +//// Map result = new HashMap<>(); +//// result.put("isLogin", true); +//// result.put("loginId", loginId); +//// result.put("tokenTimeout", timeout); +//// result.put("tokenActive", StpUtil.getTokenActiveTimeout()); +//// result.put("sessionTimeout", StpUtil.getTokenSessionTimeout()); +// +// // 添加用户基本信息 +//// addUserInfoToResult(result, loginId, identity); +// +// return SaResult.ok("token验证成功").setData(Boolean.TRUE); +// +// } catch (Exception e) { +// // 记录日志 +// // log.error("token验证异常: {}", e.getMessage(), e); +// return SaResult.error("token验证异常: " + e.getMessage()).setCode(500); +// } +// +// } + +// // 会话是否登录 +// @GetMapping("/sso/isLogin") +// public SaResult isLogin() { +// StpUtil.getTokenValue(); +// +// return SaResult.ok("验证成功!").setData(StpUtil.isLogin()); +// } + + + // 会话是否登录 + @GetMapping("/sso/isLogin") + public SaResult isLogin() { + StpUtil.getTokenValue(); +// System.out.println( StpUtil.getTokenValue()); +// if (token != null && !token.trim().isEmpty()) { +// StpUtil.setTokenValue(token); +// } + return SaResult.ok("验证成功!").setData(StpUtil.isLogin()); + } + + /** + * 验证用户身份类型是否匹配 + */ + private boolean validateUserIdentity(Object loginId, Integer identity, String token) { + try { + // 从token中获取prv字段进行验证 + String prv = StpUtil.getExtra("prv", "").toString(); + + if (identity == 1) { + // 用户身份验证 + return SystemConstant.JWT_PRV_USER.equals(prv); + } else if (identity == 2) { + // 管理员身份验证 + return SystemConstant.JWT_PRV_ADMIN_USER.equals(prv); + } + + return false; + } catch (Exception e) { + // 如果无法从token中获取prv,则通过数据库验证 + return validateUserIdentityFromDB(loginId, identity); + } + } + + /** + * 通过数据库验证用户身份 + */ + private boolean validateUserIdentityFromDB(Object loginId, Integer identity) { + try { + Long userId = Long.parseLong(loginId.toString()); + + if (identity == 1) { + User user = userService.find(userId); + return user != null && user.getIsLock() == 0; // 同时验证是否被锁定 + } else if (identity == 2) { + AdminUser adminUser = adminUserService.findById(userId); + return adminUser != null && adminUser.getIsBanLogin() == 0; // 验证是否被禁止登录 + } + + return false; + } catch (Exception e) { + return false; + } + } + + + +} diff --git a/src/main/java/com/ngskcloud/entity/AdminUser.java b/src/main/java/com/ngskcloud/entity/AdminUser.java new file mode 100644 index 0000000..b96d123 --- /dev/null +++ b/src/main/java/com/ngskcloud/entity/AdminUser.java @@ -0,0 +1,174 @@ +package com.ngskcloud.entity;/* + * + * + * 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. + */ + + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + + +import java.io.Serializable; +import java.util.Date; + +/** + * @TableName admin_users + */ +@TableName(value = "admin_users") +@Data +@Slf4j +public class AdminUser implements Serializable { + /** */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** 姓名 */ + private String name; + + /** 邮箱 */ + private String email; + + /** 密码 */ + @JsonIgnore private String password; + + /** Salt */ + @JsonIgnore private String salt; + + /** 登录IP */ + @JsonProperty("login_ip") + private String loginIp; + + /** 登录时间 */ + @JsonProperty("login_at") + private Date loginAt; + + /** 1禁止登录,0否 */ + @JsonProperty("is_ban_login") + private Integer isBanLogin; + + /** 登录次数 */ + @JsonProperty("login_times") + private Integer loginTimes; + /** 登陆账号 */ + @JsonProperty("account") + private String account; + + @JsonProperty("created_at") + private Date createdAt; + + @JsonProperty("updated_at") + private Date updatedAt; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + +// @JsonGetter("email") +// public String transformEmail() { +// return BackendBus.valueHidden( +// BPermissionConstant.DATA_ADMIN_EMAIL, +// BackendConstant.PRIVACY_FIELD_TYPE_EMAIL, +// email); +// } + + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that == null) { + return false; + } + if (getClass() != that.getClass()) { + return false; + } + AdminUser other = (AdminUser) that; + return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId())) + && (this.getName() == null + ? other.getName() == null + : this.getName().equals(other.getName())) + && (this.getEmail() == null + ? other.getEmail() == null + : this.getEmail().equals(other.getEmail())) + && (this.getPassword() == null + ? other.getPassword() == null + : this.getPassword().equals(other.getPassword())) + && (this.getSalt() == null + ? other.getSalt() == null + : this.getSalt().equals(other.getSalt())) + && (this.getLoginIp() == null + ? other.getLoginIp() == null + : this.getLoginIp().equals(other.getLoginIp())) + && (this.getLoginAt() == null + ? other.getLoginAt() == null + : this.getLoginAt().equals(other.getLoginAt())) + && (this.getIsBanLogin() == null + ? other.getIsBanLogin() == null + : this.getIsBanLogin().equals(other.getIsBanLogin())) + && (this.getLoginTimes() == null + ? other.getLoginTimes() == null + : this.getLoginTimes().equals(other.getLoginTimes())) + && (this.getCreatedAt() == null + ? other.getCreatedAt() == null + : this.getCreatedAt().equals(other.getCreatedAt())) + && (this.getUpdatedAt() == null + ? other.getUpdatedAt() == null + : this.getUpdatedAt().equals(other.getUpdatedAt())); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((getId() == null) ? 0 : getId().hashCode()); + result = prime * result + ((getName() == null) ? 0 : getName().hashCode()); + result = prime * result + ((getEmail() == null) ? 0 : getEmail().hashCode()); + result = prime * result + ((getPassword() == null) ? 0 : getPassword().hashCode()); + result = prime * result + ((getSalt() == null) ? 0 : getSalt().hashCode()); + result = prime * result + ((getLoginIp() == null) ? 0 : getLoginIp().hashCode()); + result = prime * result + ((getLoginAt() == null) ? 0 : getLoginAt().hashCode()); + result = prime * result + ((getIsBanLogin() == null) ? 0 : getIsBanLogin().hashCode()); + result = prime * result + ((getLoginTimes() == null) ? 0 : getLoginTimes().hashCode()); + result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode()); + result = prime * result + ((getUpdatedAt() == null) ? 0 : getUpdatedAt().hashCode()); + return result; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append(" ["); + sb.append("Hash = ").append(hashCode()); + sb.append(", id=").append(id); + sb.append(", name=").append(name); + sb.append(", email=").append(email); + sb.append(", password=").append(password); + sb.append(", salt=").append(salt); + sb.append(", loginIp=").append(loginIp); + sb.append(", loginAt=").append(loginAt); + sb.append(", isBanLogin=").append(isBanLogin); + sb.append(", loginTimes=").append(loginTimes); + sb.append(", createdAt=").append(createdAt); + sb.append(", updatedAt=").append(updatedAt); + sb.append(", serialVersionUID=").append(serialVersionUID); + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/main/java/com/ngskcloud/entity/User.java b/src/main/java/com/ngskcloud/entity/User.java new file mode 100644 index 0000000..5cd1949 --- /dev/null +++ b/src/main/java/com/ngskcloud/entity/User.java @@ -0,0 +1,266 @@ +package com.ngskcloud.entity;/* + * + * + * 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. + */ + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; + + + +import java.io.Serializable; +import java.util.Date; + +/** + * @TableName users + */ +@TableName(value = "users") +@Data +public class User implements Serializable { + + /** */ + @TableId(type = IdType.ASSIGN_ID) + @JsonFormat(shape = JsonFormat.Shape.STRING) + private Long id; + + /** 邮件 */ + private String email; + + /** 真实姓名 */ + private String name; + + /** 头像 */ + private String avatar; + + /** 密码 */ + @JsonIgnore private String password; + + /** salt */ + @JsonIgnore private String salt; + + /** 身份证号 */ + @JsonProperty("id_card") + private String idCard; + + /** 学分 */ + private Integer credit1; + + /** 注册Ip */ + @JsonProperty("create_ip") + private String createIp; + + /** 注册城市 */ + @JsonProperty("create_city") + private String createCity; + + /** 激活[1:是,0:否] */ + @JsonProperty("is_active") + private Integer isActive; + + /** 锁定[1:是,0:否] */ + @JsonProperty("is_lock") + private Integer isLock; + + /** 实名认证[1:是,0:否] */ + @JsonProperty("is_verify") + private Integer isVerify; + + /** 实名认证时间 */ + @JsonProperty("verify_at") + private Date verifyAt; + + /** 设置密码[1:是,0:否] */ + @JsonProperty("is_set_password") + private Integer isSetPassword; + + /** 登录时间 */ + @JsonProperty("login_at") + private Date loginAt; + + @JsonProperty("created_at") + private Date createdAt; + + @JsonProperty("updated_at") + private Date updatedAt; + @TableLogic + private Integer isDeleted; + /** 同步状态 0-未同步 1-同步成功 2-同步失败 */ + @JsonProperty("sync_status") + private int syncStatus; + /** 同步失败原因 */ + @JsonProperty("sync_remark") + private String syncRemark; + + /** 同步时间 */ + @JsonProperty("sync_time") + private Date syncTime; + + /** gitea账号 */ + @JsonProperty("gitea_user") + private String giteaUser; + /** 账号 */ + @JsonProperty("account") + private String account; + /** 管理员关联账号 */ + @JsonProperty("admin_user") + private Integer adminUser; + +// @TableField(exist = false) +// private List userSyncInfoList; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + +// @JsonGetter("name") +// public String transformName() { +// return BackendBus.valueHidden( +// BPermissionConstant.DATA_USER_NAME, +// BackendConstant.PRIVACY_FIELD_TYPE_NAME, +// getName()); +// } + +// @JsonGetter("email") +// public String transformEmail() { +// return BackendBus.valueHidden( +// BPermissionConstant.DATA_USER_EMAIL, +// BackendConstant.PRIVACY_FIELD_TYPE_EMAIL, +// getEmail()); +// } + + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that == null) { + return false; + } + if (getClass() != that.getClass()) { + return false; + } + User other = (User) that; + return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId())) + && (this.getEmail() == null + ? other.getEmail() == null + : this.getEmail().equals(other.getEmail())) + && (this.getName() == null + ? other.getName() == null + : this.getName().equals(other.getName())) + && (this.getAvatar() == null + ? other.getAvatar() == null + : this.getAvatar().equals(other.getAvatar())) + && (this.getPassword() == null + ? other.getPassword() == null + : this.getPassword().equals(other.getPassword())) + && (this.getSalt() == null + ? other.getSalt() == null + : this.getSalt().equals(other.getSalt())) + && (this.getIdCard() == null + ? other.getIdCard() == null + : this.getIdCard().equals(other.getIdCard())) + && (this.getCredit1() == null + ? other.getCredit1() == null + : this.getCredit1().equals(other.getCredit1())) + && (this.getCreateIp() == null + ? other.getCreateIp() == null + : this.getCreateIp().equals(other.getCreateIp())) + && (this.getCreateCity() == null + ? other.getCreateCity() == null + : this.getCreateCity().equals(other.getCreateCity())) + && (this.getIsActive() == null + ? other.getIsActive() == null + : this.getIsActive().equals(other.getIsActive())) + && (this.getIsLock() == null + ? other.getIsLock() == null + : this.getIsLock().equals(other.getIsLock())) + && (this.getIsVerify() == null + ? other.getIsVerify() == null + : this.getIsVerify().equals(other.getIsVerify())) + && (this.getVerifyAt() == null + ? other.getVerifyAt() == null + : this.getVerifyAt().equals(other.getVerifyAt())) + && (this.getIsSetPassword() == null + ? other.getIsSetPassword() == null + : this.getIsSetPassword().equals(other.getIsSetPassword())) + && (this.getLoginAt() == null + ? other.getLoginAt() == null + : this.getLoginAt().equals(other.getLoginAt())) + && (this.getCreatedAt() == null + ? other.getCreatedAt() == null + : this.getCreatedAt().equals(other.getCreatedAt())) + && (this.getUpdatedAt() == null + ? other.getUpdatedAt() == null + : this.getUpdatedAt().equals(other.getUpdatedAt())); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((getId() == null) ? 0 : getId().hashCode()); + result = prime * result + ((getEmail() == null) ? 0 : getEmail().hashCode()); + result = prime * result + ((getName() == null) ? 0 : getName().hashCode()); + result = prime * result + ((getAvatar() == null) ? 0 : getAvatar().hashCode()); + result = prime * result + ((getPassword() == null) ? 0 : getPassword().hashCode()); + result = prime * result + ((getSalt() == null) ? 0 : getSalt().hashCode()); + result = prime * result + ((getIdCard() == null) ? 0 : getIdCard().hashCode()); + result = prime * result + ((getCredit1() == null) ? 0 : getCredit1().hashCode()); + result = prime * result + ((getCreateIp() == null) ? 0 : getCreateIp().hashCode()); + result = prime * result + ((getCreateCity() == null) ? 0 : getCreateCity().hashCode()); + result = prime * result + ((getIsActive() == null) ? 0 : getIsActive().hashCode()); + result = prime * result + ((getIsLock() == null) ? 0 : getIsLock().hashCode()); + result = prime * result + ((getIsVerify() == null) ? 0 : getIsVerify().hashCode()); + result = prime * result + ((getVerifyAt() == null) ? 0 : getVerifyAt().hashCode()); + result = + prime * result + ((getIsSetPassword() == null) ? 0 : getIsSetPassword().hashCode()); + result = prime * result + ((getLoginAt() == null) ? 0 : getLoginAt().hashCode()); + result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode()); + result = prime * result + ((getUpdatedAt() == null) ? 0 : getUpdatedAt().hashCode()); + return result; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append(" ["); + sb.append("Hash = ").append(hashCode()); + sb.append(", id=").append(id); + sb.append(", email=").append(email); + sb.append(", name=").append(name); + sb.append(", avatar=").append(avatar); + sb.append(", password=").append(password); + sb.append(", salt=").append(salt); + sb.append(", idCard=").append(idCard); + sb.append(", credit1=").append(credit1); + sb.append(", createIp=").append(createIp); + sb.append(", createCity=").append(createCity); + sb.append(", isActive=").append(isActive); + sb.append(", isLock=").append(isLock); + sb.append(", isVerify=").append(isVerify); + sb.append(", verifyAt=").append(verifyAt); + sb.append(", isSetPassword=").append(isSetPassword); + sb.append(", loginAt=").append(loginAt); + sb.append(", createdAt=").append(createdAt); + sb.append(", updatedAt=").append(updatedAt); + sb.append(", serialVersionUID=").append(serialVersionUID); + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/main/java/com/ngskcloud/feign/SsoServerClient.java b/src/main/java/com/ngskcloud/feign/SsoServerClient.java new file mode 100644 index 0000000..5fe7707 --- /dev/null +++ b/src/main/java/com/ngskcloud/feign/SsoServerClient.java @@ -0,0 +1,46 @@ +package com.ngskcloud.feign; + +import cn.dev33.satoken.util.SaResult; +import com.ngskcloud.config.FeignConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.Map; + +/** + * SSO 服务端 OpenFeign 客户端接口 + * 用于其他微服务通过 Nacos 调用 SSO 功能 + */ +@FeignClient(name = "sso-server", path = "/sso", configuration = FeignConfig.class) +public interface SsoServerClient { + + + /** + * 登录接口 + */ + @GetMapping("/doLogin") + SaResult doLogin(@RequestParam("username") String account, + @RequestParam("pwd") String pwd, + @RequestParam("identity") Integer identity); + + /** + * 验证ticket接口 + */ + @GetMapping("/checkTicket") + SaResult checkTicket(@RequestParam("ticket") String ticket, + @RequestParam("identity") Integer identity); + + /** + * 检查登录状态 + */ + @GetMapping("/isLogin") + SaResult isLogin(); + + /** + * 注销接口 + */ + @PostMapping("/logout") + SaResult logout(); +} diff --git a/src/main/java/com/ngskcloud/interceptor/SaTokenInterceptorConfig.java b/src/main/java/com/ngskcloud/interceptor/SaTokenInterceptorConfig.java new file mode 100644 index 0000000..3149f04 --- /dev/null +++ b/src/main/java/com/ngskcloud/interceptor/SaTokenInterceptorConfig.java @@ -0,0 +1,18 @@ +package com.ngskcloud.interceptor; + +import cn.dev33.satoken.interceptor.SaInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class SaTokenInterceptorConfig implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 注册 Sa-Token 拦截器,打开注解式鉴权功能 + registry.addInterceptor(new SaInterceptor()) + .addPathPatterns("/**") + .excludePathPatterns("/sso/**", "/error"); + } +} \ No newline at end of file diff --git a/src/main/java/com/ngskcloud/mapper/AdminUserMapper.java b/src/main/java/com/ngskcloud/mapper/AdminUserMapper.java new file mode 100644 index 0000000..b42006d --- /dev/null +++ b/src/main/java/com/ngskcloud/mapper/AdminUserMapper.java @@ -0,0 +1,10 @@ +package com.ngskcloud.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ngskcloud.entity.AdminUser; +import org.apache.ibatis.annotations.Mapper; + +//@Mapper +public interface AdminUserMapper extends BaseMapper { + +} diff --git a/src/main/java/com/ngskcloud/mapper/AdminUserMapper.xml b/src/main/java/com/ngskcloud/mapper/AdminUserMapper.xml new file mode 100644 index 0000000..7f598c6 --- /dev/null +++ b/src/main/java/com/ngskcloud/mapper/AdminUserMapper.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + id + ,name,email, + password,salt,login_ip, + login_at,is_ban_login,login_times, + created_at,updated_at + + diff --git a/src/main/java/com/ngskcloud/mapper/UserMapper.java b/src/main/java/com/ngskcloud/mapper/UserMapper.java new file mode 100644 index 0000000..eaabc3d --- /dev/null +++ b/src/main/java/com/ngskcloud/mapper/UserMapper.java @@ -0,0 +1,10 @@ +package com.ngskcloud.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ngskcloud.entity.User; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface UserMapper extends BaseMapper { + +} diff --git a/src/main/java/com/ngskcloud/mapper/UserMapper.xml b/src/main/java/com/ngskcloud/mapper/UserMapper.xml new file mode 100644 index 0000000..8a06cb8 --- /dev/null +++ b/src/main/java/com/ngskcloud/mapper/UserMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/java/com/ngskcloud/service/AdminUserService.java b/src/main/java/com/ngskcloud/service/AdminUserService.java new file mode 100644 index 0000000..41c1f96 --- /dev/null +++ b/src/main/java/com/ngskcloud/service/AdminUserService.java @@ -0,0 +1,12 @@ +package com.ngskcloud.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ngskcloud.entity.AdminUser; + + +public interface AdminUserService extends IService { + AdminUser findByAccount(String account); + + AdminUser findById(Long id); + +} diff --git a/src/main/java/com/ngskcloud/service/Impl/AdminUserServiceImpl.java b/src/main/java/com/ngskcloud/service/Impl/AdminUserServiceImpl.java new file mode 100644 index 0000000..8c65341 --- /dev/null +++ b/src/main/java/com/ngskcloud/service/Impl/AdminUserServiceImpl.java @@ -0,0 +1,20 @@ +package com.ngskcloud.service.Impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ngskcloud.entity.AdminUser; +import com.ngskcloud.mapper.AdminUserMapper; +import com.ngskcloud.service.AdminUserService; +import org.springframework.stereotype.Service; + +@Service +public class AdminUserServiceImpl extends ServiceImpl implements AdminUserService { + + @Override + public AdminUser findByAccount(String account) { + return getOne(query().getWrapper().eq("account", account)); + } + @Override + public AdminUser findById(Long id) { + return getOne((query().getWrapper().eq("id", id))); + } +} diff --git a/src/main/java/com/ngskcloud/service/Impl/UserServiceImpl.java b/src/main/java/com/ngskcloud/service/Impl/UserServiceImpl.java new file mode 100644 index 0000000..31f3aa1 --- /dev/null +++ b/src/main/java/com/ngskcloud/service/Impl/UserServiceImpl.java @@ -0,0 +1,22 @@ +package com.ngskcloud.service.Impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ngskcloud.entity.User; +import com.ngskcloud.mapper.UserMapper; +import com.ngskcloud.service.UserService; +import org.springframework.stereotype.Service; + +@Service +public class UserServiceImpl extends ServiceImpl implements UserService { + + + @Override + public User find(String account) { + return getOne(query().getWrapper().eq("account", account)); + } + + @Override + public User find(Long id) { + return getOne(query().getWrapper().eq("id", id)); + } +} diff --git a/src/main/java/com/ngskcloud/service/UserService.java b/src/main/java/com/ngskcloud/service/UserService.java new file mode 100644 index 0000000..7571e3c --- /dev/null +++ b/src/main/java/com/ngskcloud/service/UserService.java @@ -0,0 +1,12 @@ +package com.ngskcloud.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ngskcloud.entity.User; +import org.springframework.stereotype.Service; + + +public interface UserService extends IService { + User find(String account); + User find(Long id); + +} diff --git a/src/main/java/com/ngskcloud/utils/DigestUtil.java b/src/main/java/com/ngskcloud/utils/DigestUtil.java new file mode 100644 index 0000000..bfdfb4c --- /dev/null +++ b/src/main/java/com/ngskcloud/utils/DigestUtil.java @@ -0,0 +1,62 @@ +package com.ngskcloud.utils; +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +import com.alibaba.cloud.commons.lang.StringUtils; +import org.apache.commons.codec.Charsets; +import org.springframework.util.DigestUtils; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + + +public class DigestUtil { + + private static final char[] HEX_CODE = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + public static String encrypt(String data) { + return StringUtils.isBlank(data) ? "" : sha1Hex(md5Hex(data)); + } + + + public static String md5Hex(final String data) { + return DigestUtils.md5DigestAsHex(data.getBytes(Charsets.UTF_8)); + } + + public static String sha1Hex(String data) { + return sha1Hex((data.getBytes(Charsets.UTF_8))); + } + + public static String sha1Hex(final byte[] bytes) { + return digestHex("SHA-1", bytes); + } + + public static String digestHex(String algorithm, byte[] bytes) { + try { + MessageDigest md = MessageDigest.getInstance(algorithm); + return encodeHex(md.digest(bytes)); + } catch (NoSuchAlgorithmException var3) { + throw new RuntimeException(var3.getMessage()); + } + } + + public static String encodeHex(byte[] bytes) { + StringBuilder r = new StringBuilder(bytes.length * 2); + byte[] var2 = bytes; + int var3 = bytes.length; + + for(int var4 = 0; var4 < var3; ++var4) { + byte b = var2[var4]; + r.append(HEX_CODE[b >> 4 & 15]); + r.append(HEX_CODE[b & 15]); + } + + return r.toString(); + } + + public static String hex(String data) { + return StringUtils.isBlank(data) ? "" : sha1Hex(data); + } +} diff --git a/src/main/java/com/ngskcloud/utils/JwtUtil.java b/src/main/java/com/ngskcloud/utils/JwtUtil.java new file mode 100644 index 0000000..f5ba30f --- /dev/null +++ b/src/main/java/com/ngskcloud/utils/JwtUtil.java @@ -0,0 +1,222 @@ +package com.ngskcloud.utils; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * JWT工具类 + * 用于生成、解析和验证JWT Token + */ +@Component +public class JwtUtil { + // JWT签名密钥,从配置文件读取 + @Value("${jwt.secret:your-secret-key-here-make-it-long-enough}") + private String secret; + + // Token过期时间(默认1小时) + @Value("${jwt.expiration:3600000}") + private Long expiration; + + // Token前缀 + @Value("${jwt.tokenHead:Bearer }") + private String tokenHead; + + /** + * 生成JWT Token + * + * @param username 用户名 + * @return JWT Token字符串 + */ + public String generateToken(String username) { + Map claims = buildClaims(username); + return generateToken(claims); + } + + /** + * 生成包含自定义声明的JWT Token + * + * @param claims 自定义声明 + * @return JWT Token字符串 + */ + public String generateToken(Map claims) { + return Jwts.builder() + .setHeaderParam("typ", "JWT") + .setHeaderParam("alg", "HS256") + .setClaims(claims) + .setExpiration(generateExpirationDate()) + .setIssuedAt(new Date()) + .signWith(SignatureAlgorithm.HS256, secret) + .compact(); + } + + /** + * 从Token中获取声明信息 + * + * @param token JWT Token + * @return Claims声明对象 + */ + public Claims getClaimsFromToken(String token) { + try { + return Jwts.parser() + .verifyWith(getSigningKey()) // 使用 verifyWith 替代 setSigningKey + .build() + .parseSignedClaims(token) // 使用 parseSignedClaims 替代 parseClaimsJws + .getPayload(); // 使用 getPayload() 替代 getBody() + } catch (Exception e) { + throw new RuntimeException("JWT格式验证失败", e); + } + } + + // 获取签名密钥的辅助方法 + private SecretKey getSigningKey() { + // 确保您的密钥是 SecretKey 类型,而不是普通的 Key + return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); + } + /** + * 验证Token是否有效 + * + * @param token JWT Token + * @return 是否有效 + */ + public boolean validateToken(String token) { + try { + getClaimsFromToken(token); + return true; + } catch (Exception e) { + return false; + } + } + + /** + * 判断Token是否过期 + * + * @param token JWT Token + * @return 是否过期 + */ + public boolean isTokenExpired(String token) { + Date expirationDate = getExpirationDateFromToken(token); + return expirationDate.before(new Date()); + } + + /** + * 从Token中获取过期时间 + * + * @param token JWT Token + * @return 过期时间 + */ + public Date getExpirationDateFromToken(String token) { + Claims claims = getClaimsFromToken(token); + return claims.getExpiration(); + } + + /** + * 从Token中获取用户名 + * + * @param token JWT Token + * @return 用户名 + */ + public String getUsernameFromToken(String token) { + Claims claims = getClaimsFromToken(token); + return claims.get("user_name", String.class); + } + + /** + * 刷新Token(重新生成,保留原有声明) + * + * @param token 原Token + * @return 新Token + */ + public String refreshToken(String token) { + Claims oldClaims = getClaimsFromToken(token); + + // 创建新的 Claims 构建器,复制原有声明并更新时间 + return Jwts.builder() + .claims(oldClaims) // 复制原有声明 + .issuedAt(new Date()) // 设置新的签发时间 + .expiration(generateExpirationDate()) // 设置新的过期时间 + .signWith(getSigningKey()) // 签名 + .compact(); + } + + /** + * 构建标准声明信息 + * + * @param username 用户名 + * @return 声明Map + */ + private Map buildClaims(String username) { + Map claims = new HashMap<>(); + + // 用户基本信息 + claims.put("tenant_id", "000000"); + claims.put("user_name", username); + claims.put("real_name", "管理员"); + claims.put("avatar", "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamRoxxVxka.png"); + claims.put("authorities", new String[]{"administrator"}); + claims.put("client_id", "isdp2"); + claims.put("role_name", "administrator"); + claims.put("license", "powered by bladex"); + claims.put("post_id", "1769936598361804802"); + claims.put("user_id", "1123598821738675201"); + claims.put("role_id", "1123598816738675201"); + claims.put("scope", new String[]{"all"}); + claims.put("nick_name", username); + claims.put("oauth_id", ""); + claims.put("account", username); + claims.put("dept_id", "1769934936519839746"); + claims.put("jti", "6575dce1-26ce-433a-bd2f-e792e9067ba4"); + + // 添加detail信息 + Map detail = new HashMap<>(); + detail.put("type", "web"); + claims.put("detail", detail); + + return claims; + } + + /** + * 生成过期时间 + * + * @return 过期时间 + */ + private Date generateExpirationDate() { + return new Date(System.currentTimeMillis() + expiration); + } + + // Getter和Setter方法 + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + public Long getExpiration() { + return expiration; + } + + public void setExpiration(Long expiration) { + this.expiration = expiration; + } + + public String getTokenHead() { + return tokenHead; + } + + public void setTokenHead(String tokenHead) { + this.tokenHead = tokenHead; + } + + +} diff --git a/src/main/java/com/ngskcloud/utils/RequestUtil.java b/src/main/java/com/ngskcloud/utils/RequestUtil.java new file mode 100644 index 0000000..0c1ef7e --- /dev/null +++ b/src/main/java/com/ngskcloud/utils/RequestUtil.java @@ -0,0 +1,106 @@ +/* + * + * + * 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 com.ngskcloud.utils; + +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; + +import jakarta.servlet.http.HttpServletRequest; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.util.Arrays; +import java.util.List; + +public class RequestUtil { + + public static HttpServletRequest handler() { + ServletRequestAttributes servletRequestAttributes = + (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + return servletRequestAttributes == null ? null : servletRequestAttributes.getRequest(); + } + + public static UserAgent ua() { + HttpServletRequest request = RequestUtil.handler(); + if (request == null) { + return null; + } + return UserAgentUtil.parse(request.getHeader("User-Agent")); + } + + public static String token() { + HttpServletRequest request = RequestUtil.handler(); + if (request == null) { + return ""; + } + String token = request.getHeader("Authorization"); + if (token == null || token.length() == 0 || token.split(" ").length != 2) { + return ""; + } + return token.split(" ")[1]; + } + + public static String url() { + HttpServletRequest request = RequestUtil.handler(); + return request == null ? "" : request.getRequestURL().toString(); + } + + public static String uri() { + Integer portNumber = port(); + return RequestUtil.domain() + + (Arrays.asList(443, 80, 0).contains(portNumber) ? "" : ":" + portNumber); + } + + public static String uriWithProtocol() { + Integer portNumber = port(); + return RequestUtil.protocol() + + RequestUtil.domain() + + (Arrays.asList(443, 80, 0).contains(portNumber) ? "" : ":" + portNumber); + } + + public static String pathname() { + HttpServletRequest request = RequestUtil.handler(); + return request == null ? "" : request.getRequestURI(); + } + + public static Integer port() { + HttpServletRequest request = RequestUtil.handler(); + return request == null ? 0 : request.getServerPort(); + } + + public static String domain() { + HttpServletRequest request = RequestUtil.handler(); + if (request != null) { + String requestUrl = request.getRequestURL().toString(); + List urls = Arrays.asList(requestUrl.split("/")); + + return urls.get(2).split(":")[0]; + } + return null; + } + + public static String protocol() { + HttpServletRequest request = RequestUtil.handler(); + if (request != null) { + String requestUrl = request.getRequestURL().toString(); + List urls = Arrays.asList(requestUrl.split("//")); + + return urls.get(0) + "//"; + } + return null; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..955f7cf --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,112 @@ +server: + # 端口 + port: 8027 + +############## Sa-Token 配置 (文档: https://sa-token.cc) ############## +sa-token: + # token 名称(同时也是 cookie 名称) + token-name: satoken + # token 有效期(单位:秒) 默认30天,-1 代表永久有效 + timeout: 1296000 + # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结 + active-timeout: -1 + # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录) + is-concurrent: true + # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token) + is-share: false + jwt-secret-key: "playeduxyz" + token-prefix: "Bearer" + # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik) +# token-style: jwt + is-log: true + sso-server: + is-slo: true + # Ticket有效期 (单位: 秒),默认五分钟 + ticket-timeout: 3000 + # 应用列表:配置接入的应用信息 + clients: + # 应用 sso-client2:采用模式三对接 + sso-client2: + client: playedu-client + allow-url: "*" + secret-key: SSO-C2-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor + + + # MyBatis-Plus配置 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + global-config: + db-config: + id-type: auto + logic-delete-field: deleted + logic-delete-value: 1 + logic-not-delete-value: 0 + +spring: + datasource: + # 移除type配置,使用默认的HikariCP + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://192.168.5.119:3306/video?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghai + username: root + password: ngskcloud0809 + hikari: + maximum-pool-size: 20 + minimum-idle: 5 + idle-timeout: 300000 + connection-timeout: 20000 + max-lifetime: 1200000 + main: + allow-circular-references: true # 临时允许循环依赖 + cache: + type: redis # 缓存配置,这里集成了redis和ehcache两种缓存方式,基本是使用redis。 + ehcache: + config: classpath:config/ehcache.xml + application: + name: bpms-sso # 服务名称 + profiles: + active: dev #启动环境 dev:开发环境 local:本地环境(需要将EnableTurbine注释掉) + cloud: + nacos: + discovery: + server-addr: http://192.168.5.119:8848 + namespace: ng + username: nacos + password: ngsk0809 + config: + server-addr: http://192.168.5.119:8848 + namespace: ng + file-extension: yml + username: nacos + password: ngsk0809 + import-check: + enabled: false + data: + redis: + open: true + host: 192.168.5.119 # 需替换 + port: 6379 # 需替换 + database: 0 # 需替换 + timeout: 6000 + password: ngsk0809 # 需替换 + lettuce: + cluster: + refresh: + adaptive: true + period: 20 + shutdown-timeout: 6000ms + pool: + max-active: 20 + max-idle: 10 + max-wait: -1ms + min-idle: 5 + template: + defaultSerializer: + org.springframework.data.redis.serializer.StringRedisSerializer + + +jwt: + secret: bladexisapowerfulmicroservicearchitectureupgradedandoptimizedfromacommercialproject + expiration: 3600000 # 1小时,单位毫秒 + tokenHead: "Bearer " \ No newline at end of file diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html new file mode 100644 index 0000000..89bb8ba --- /dev/null +++ b/src/main/resources/static/index.html @@ -0,0 +1,6 @@ + + +

hello word!!!

+

this is a html page

+ + \ No newline at end of file diff --git a/src/test/java/com/ngskcloud/BpmsSsoApplicationTests.java b/src/test/java/com/ngskcloud/BpmsSsoApplicationTests.java new file mode 100644 index 0000000..7270f0a --- /dev/null +++ b/src/test/java/com/ngskcloud/BpmsSsoApplicationTests.java @@ -0,0 +1,13 @@ +package com.ngskcloud; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class BpmsSsoApplicationTests { + + @Test + void contextLoads() { + } + +}