提交
This commit is contained in:
@@ -37,20 +37,20 @@
|
||||
</dependency>-->
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-data-permission</artifactId>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.tashow.cloud</groupId>-->
|
||||
<!-- <artifactId>tashow-data-permission</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-security</artifactId>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.tashow.cloud</groupId>-->
|
||||
<!-- <artifactId>tashow-framework-security</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
@@ -137,47 +137,50 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-reactor-spring-boot3-starter</artifactId>
|
||||
<version>1.42.0</version>
|
||||
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
<version>1.42.0</version>
|
||||
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 插件:整合SSO -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-sso</artifactId>
|
||||
<version>1.42.0</version>
|
||||
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token OAuth2.0 模块 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-oauth2</artifactId>
|
||||
<version>1.42.0</version>
|
||||
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redis-jackson</artifactId>
|
||||
<version>1.42.0</version>
|
||||
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.dtflys.forest</groupId>
|
||||
<artifactId>forest-spring-boot-starter</artifactId>
|
||||
<version>1.5.26</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-alone-redis</artifactId>
|
||||
<version>1.42.0</version>
|
||||
</dependency>
|
||||
<!-- 视图引擎(在前后端不分离模式下提供视图支持) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -6,10 +6,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class TashowModuleSsoBizApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TashowModuleSsoBizApplication.class, args);
|
||||
System.out.println("\nSa-Token-OAuth2 Server端启动成功,配置如下:");
|
||||
System.out.println("Sa-Token-OAuth2 Server端启动成功,配置如下:");
|
||||
System.out.println(SaOAuth2Manager.getServerConfig());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.tashow.cloud.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* MyBatis Plus配置类
|
||||
*/
|
||||
@Configuration
|
||||
@MapperScan("com.tashow.cloud.mapper")
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
/**
|
||||
* 配置分页插件
|
||||
*/
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 添加分页插件
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
@@ -1,377 +0,0 @@
|
||||
package com.tashow.cloud.controller;
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.oauth2.config.SaOAuth2ServerConfig;
|
||||
import cn.dev33.satoken.oauth2.consts.GrantType;
|
||||
import cn.dev33.satoken.oauth2.data.model.loader.SaClientModel;
|
||||
import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import com.tashow.cloud.model.SystemUser;
|
||||
import com.tashow.cloud.service.SystemUserService;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Sa-Token OAuth2 Server端 控制器
|
||||
*/
|
||||
@RestController
|
||||
public class SaOAuth2ServerController {
|
||||
|
||||
@Autowired
|
||||
private SystemUserService userService;
|
||||
|
||||
// OAuth2-Server 端:处理所有 OAuth2 相关请求
|
||||
@RequestMapping("/oauth2/*")
|
||||
@PermitAll
|
||||
public Object request() {
|
||||
System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl());
|
||||
return SaOAuth2ServerProcessor.instance.dister();
|
||||
}
|
||||
|
||||
// Sa-Token OAuth2 定制化配置
|
||||
@Autowired
|
||||
@PermitAll
|
||||
public void configOAuth2Server(SaOAuth2ServerConfig oauth2Server) {
|
||||
|
||||
// 添加 client 信息
|
||||
oauth2Server.addClient(
|
||||
new SaClientModel()
|
||||
.setClientId("1001") // client id
|
||||
.setClientSecret("aaaa-bbbb-cccc-dddd-eeee") // client 秘钥
|
||||
.addAllowRedirectUris("*") // 所有允许授权的 url
|
||||
.addContractScopes("openid", "userid", "userinfo") // 所有签约的权限
|
||||
.addAllowGrantTypes( // 所有允许的授权模式
|
||||
GrantType.authorization_code, // 授权码式
|
||||
GrantType.implicit, // 隐式式
|
||||
GrantType.refresh_token, // 刷新令牌
|
||||
GrantType.password, // 密码式
|
||||
GrantType.client_credentials // 客户端模式
|
||||
)
|
||||
);
|
||||
|
||||
// 可以添加更多 client 信息,只要保持 clientId 唯一就行了
|
||||
// oauth2Server.addClient(...)
|
||||
|
||||
// 配置:未登录时返回的View
|
||||
oauth2Server.notLoginView = () -> {
|
||||
String msg = "当前会话在OAuth-Server端尚未登录,请先访问"
|
||||
+ "<a href='/oauth2/login-page' target='_blank'>登录页面</a>"
|
||||
+ "或 <a href='/sso/register-page' target='_blank'>注册用户</a>"
|
||||
+ ",进行登录之后,刷新页面开始授权";
|
||||
return msg;
|
||||
};
|
||||
|
||||
// 配置:登录处理函数
|
||||
oauth2Server.doLoginHandle = (name, pwd) -> {
|
||||
// 从数据库查询用户
|
||||
SystemUser user = userService.getUserByUsername(name);
|
||||
|
||||
// 验证用户是否存在且密码是否正确
|
||||
if(user != null && userService.validatePassword(pwd, user.getPassword())) {
|
||||
// 用户状态检查
|
||||
if(user.getStatus() != 0) {
|
||||
return SaResult.error("账号已被禁用");
|
||||
}
|
||||
|
||||
// 登录成功,记录用户IP
|
||||
StpUtil.login(user.getId());
|
||||
|
||||
// 获取当前的HTTP请求对象
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
String ip = "unknown";
|
||||
if (attributes != null) {
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
ip = getClientIp(request);
|
||||
}
|
||||
|
||||
userService.updateLoginInfo(user.getId(), ip);
|
||||
|
||||
return SaResult.ok();
|
||||
}
|
||||
return SaResult.error("账号名或密码错误");
|
||||
};
|
||||
|
||||
// 配置:确认授权时返回的 view
|
||||
oauth2Server.confirmView = (clientId, scopes) -> {
|
||||
String scopeStr = SaFoxUtil.convertListToString(scopes);
|
||||
String yesCode =
|
||||
"fetch('/oauth2/doConfirm?client_id=" + clientId + "&scope=" + scopeStr + "', {method: 'POST'})" +
|
||||
".then(res => res.json())" +
|
||||
".then(res => location.reload())";
|
||||
String res = "<p>应用 " + clientId + " 请求授权:" + scopeStr + ",是否同意?</p>"
|
||||
+ "<p>" +
|
||||
" <button onclick=\"" + yesCode + "\">同意</button>" +
|
||||
" <button onclick='history.back()'>拒绝</button>" +
|
||||
"</p>";
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端真实IP地址
|
||||
*/
|
||||
private String getClientIp(HttpServletRequest request) {
|
||||
String ip = request.getHeader("X-Forwarded-For");
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
// 多个代理的情况,第一个IP为客户端真实IP
|
||||
if (ip != null && ip.indexOf(",") > 0) {
|
||||
ip = ip.substring(0, ip.indexOf(","));
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录页面接口 - 返回登录页面HTML
|
||||
*/
|
||||
@GetMapping("/oauth2/login-page")
|
||||
@PermitAll
|
||||
public Object loginPage() {
|
||||
String html = "<!DOCTYPE html>\n" +
|
||||
"<html lang=\"zh-CN\">\n" +
|
||||
"<head>\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
|
||||
" <title>OAuth2登录</title>\n" +
|
||||
" <style>\n" +
|
||||
" body {\n" +
|
||||
" font-family: Arial, sans-serif;\n" +
|
||||
" display: flex;\n" +
|
||||
" justify-content: center;\n" +
|
||||
" align-items: center;\n" +
|
||||
" height: 100vh;\n" +
|
||||
" margin: 0;\n" +
|
||||
" background-color: #f5f5f5;\n" +
|
||||
" }\n" +
|
||||
" .login-container {\n" +
|
||||
" background-color: white;\n" +
|
||||
" padding: 30px;\n" +
|
||||
" border-radius: 8px;\n" +
|
||||
" box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n" +
|
||||
" width: 350px;\n" +
|
||||
" }\n" +
|
||||
" h2 {\n" +
|
||||
" text-align: center;\n" +
|
||||
" margin-bottom: 20px;\n" +
|
||||
" }\n" +
|
||||
" .form-group {\n" +
|
||||
" margin-bottom: 15px;\n" +
|
||||
" }\n" +
|
||||
" label {\n" +
|
||||
" display: block;\n" +
|
||||
" margin-bottom: 5px;\n" +
|
||||
" font-weight: bold;\n" +
|
||||
" }\n" +
|
||||
" input {\n" +
|
||||
" width: 100%;\n" +
|
||||
" padding: 10px;\n" +
|
||||
" border: 1px solid #ddd;\n" +
|
||||
" border-radius: 4px;\n" +
|
||||
" box-sizing: border-box;\n" +
|
||||
" }\n" +
|
||||
" button {\n" +
|
||||
" width: 100%;\n" +
|
||||
" padding: 10px;\n" +
|
||||
" background-color: #007bff;\n" +
|
||||
" color: white;\n" +
|
||||
" border: none;\n" +
|
||||
" border-radius: 4px;\n" +
|
||||
" cursor: pointer;\n" +
|
||||
" font-size: 16px;\n" +
|
||||
" }\n" +
|
||||
" button:hover {\n" +
|
||||
" background-color: #0069d9;\n" +
|
||||
" }\n" +
|
||||
" .error-message {\n" +
|
||||
" color: red;\n" +
|
||||
" margin-top: 15px;\n" +
|
||||
" text-align: center;\n" +
|
||||
" display: none;\n" +
|
||||
" }\n" +
|
||||
" </style>\n" +
|
||||
"</head>\n" +
|
||||
"<body>\n" +
|
||||
" <div class=\"login-container\">\n" +
|
||||
" <h2>用户登录</h2>\n" +
|
||||
" <form id=\"loginForm\">\n" +
|
||||
" <div class=\"form-group\">\n" +
|
||||
" <label for=\"username\">用户名</label>\n" +
|
||||
" <input type=\"text\" id=\"username\" name=\"username\" required>\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"form-group\">\n" +
|
||||
" <label for=\"password\">密码</label>\n" +
|
||||
" <input type=\"password\" id=\"password\" name=\"password\" required>\n" +
|
||||
" </div>\n" +
|
||||
" <button type=\"submit\">登录</button>\n" +
|
||||
" </form>\n" +
|
||||
" <div id=\"errorMessage\" class=\"error-message\"></div>\n" +
|
||||
" </div>\n" +
|
||||
" <script>\n" +
|
||||
" document.getElementById('loginForm').addEventListener('submit', function(e) {\n" +
|
||||
" e.preventDefault();\n" +
|
||||
" \n" +
|
||||
" const username = document.getElementById('username').value;\n" +
|
||||
" const password = document.getElementById('password').value;\n" +
|
||||
" \n" +
|
||||
" fetch('/oauth2/doLogin?name=' + encodeURIComponent(username) + '&pwd=' + encodeURIComponent(password), {\n" +
|
||||
" method: 'POST'\n" +
|
||||
" })\n" +
|
||||
" .then(response => response.json())\n" +
|
||||
" .then(data => {\n" +
|
||||
" if (data.code === 200) {\n" +
|
||||
" window.location.href = '/oauth2/login-success';\n" +
|
||||
" } else {\n" +
|
||||
" const errorMessage = document.getElementById('errorMessage');\n" +
|
||||
" errorMessage.textContent = data.msg || '登录失败';\n" +
|
||||
" errorMessage.style.display = 'block';\n" +
|
||||
" }\n" +
|
||||
" })\n" +
|
||||
" .catch(error => {\n" +
|
||||
" console.error('登录请求失败:', error);\n" +
|
||||
" const errorMessage = document.getElementById('errorMessage');\n" +
|
||||
" errorMessage.textContent = '网络错误,请稍后重试';\n" +
|
||||
" errorMessage.style.display = 'block';\n" +
|
||||
" });\n" +
|
||||
" });\n" +
|
||||
" </script>\n" +
|
||||
"</body>\n" +
|
||||
"</html>";
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录接口 - 处理表单提交
|
||||
*/
|
||||
@PostMapping("/oauth2/login")
|
||||
@PermitAll
|
||||
public Object login(
|
||||
@RequestParam("username") String username,
|
||||
@RequestParam("password") String password,
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 查询用户
|
||||
SystemUser user = userService.getUserByUsername(username);
|
||||
|
||||
// 验证用户
|
||||
if (user == null || !userService.validatePassword(password, user.getPassword())) {
|
||||
return SaResult.error("账号或密码错误");
|
||||
}
|
||||
|
||||
// 检查用户状态
|
||||
if (user.getStatus() != 0) {
|
||||
return SaResult.error("账号已被禁用");
|
||||
}
|
||||
|
||||
// 登录
|
||||
StpUtil.login(user.getId());
|
||||
|
||||
// 更新登录信息
|
||||
String ip = getClientIp(request);
|
||||
userService.updateLoginInfo(user.getId(), ip);
|
||||
|
||||
// 返回结果
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("tokenValue", StpUtil.getTokenValue());
|
||||
result.put("tokenName", StpUtil.getTokenName());
|
||||
|
||||
return SaResult.data(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功页面 - 当未指定回调地址时的默认页面
|
||||
*/
|
||||
@GetMapping("/oauth2/login-success")
|
||||
@PermitAll
|
||||
public Object loginSuccess() {
|
||||
String html = "<!DOCTYPE html>\n" +
|
||||
"<html lang=\"zh-CN\">\n" +
|
||||
"<head>\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
|
||||
" <title>授权成功</title>\n" +
|
||||
" <style>\n" +
|
||||
" body {\n" +
|
||||
" font-family: Arial, sans-serif;\n" +
|
||||
" display: flex;\n" +
|
||||
" justify-content: center;\n" +
|
||||
" align-items: center;\n" +
|
||||
" height: 100vh;\n" +
|
||||
" margin: 0;\n" +
|
||||
" background-color: #f5f5f5;\n" +
|
||||
" flex-direction: column;\n" +
|
||||
" }\n" +
|
||||
" .success-container {\n" +
|
||||
" background-color: white;\n" +
|
||||
" padding: 30px;\n" +
|
||||
" border-radius: 8px;\n" +
|
||||
" box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n" +
|
||||
" width: 400px;\n" +
|
||||
" text-align: center;\n" +
|
||||
" }\n" +
|
||||
" h2 {\n" +
|
||||
" color: #28a745;\n" +
|
||||
" margin-bottom: 20px;\n" +
|
||||
" }\n" +
|
||||
" p {\n" +
|
||||
" margin: 10px 0;\n" +
|
||||
" color: #333;\n" +
|
||||
" }\n" +
|
||||
" .userinfo {\n" +
|
||||
" margin-top: 20px;\n" +
|
||||
" text-align: left;\n" +
|
||||
" padding: 15px;\n" +
|
||||
" background-color: #f8f9fa;\n" +
|
||||
" border-radius: 5px;\n" +
|
||||
" }\n" +
|
||||
" .button {\n" +
|
||||
" display: inline-block;\n" +
|
||||
" margin-top: 20px;\n" +
|
||||
" padding: 10px 20px;\n" +
|
||||
" background-color: #007bff;\n" +
|
||||
" color: white;\n" +
|
||||
" border: none;\n" +
|
||||
" border-radius: 4px;\n" +
|
||||
" text-decoration: none;\n" +
|
||||
" cursor: pointer;\n" +
|
||||
" }\n" +
|
||||
" .button:hover {\n" +
|
||||
" background-color: #0069d9;\n" +
|
||||
" }\n" +
|
||||
" </style>\n" +
|
||||
"</head>\n" +
|
||||
"<body>\n" +
|
||||
" <div class=\"success-container\">\n" +
|
||||
" <h2>OAuth2授权成功</h2>\n" +
|
||||
" <p>您已成功登录并授权给应用</p>\n" +
|
||||
" <div class=\"userinfo\">\n" +
|
||||
" <p>当前登录状态:已登录</p>\n" +
|
||||
" <p>令牌信息已生成</p>\n" +
|
||||
" </div>\n" +
|
||||
" <a href=\"javascript:void(0)\" onclick=\"window.close()\" class=\"button\">关闭窗口</a>\n" +
|
||||
" </div>\n" +
|
||||
"</body>\n" +
|
||||
"</html>";
|
||||
return html;
|
||||
}
|
||||
}
|
||||
@@ -1,653 +0,0 @@
|
||||
package com.tashow.cloud.controller;
|
||||
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import com.dtflys.forest.Forest;
|
||||
import com.tashow.cloud.model.SystemUser;
|
||||
import com.tashow.cloud.service.SystemUserService;
|
||||
import com.tashow.cloud.service.impl.SystemUserServiceImpl;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO Server端 Controller
|
||||
*/
|
||||
@RestController
|
||||
public class SsoServerController {
|
||||
|
||||
@Autowired
|
||||
private SystemUserService userService;
|
||||
|
||||
@Autowired
|
||||
private SystemUserServiceImpl userServiceImpl;
|
||||
|
||||
/**
|
||||
* SSO-Server端:处理所有SSO相关请求 (下面的章节我们会详细列出开放的接口)
|
||||
*/
|
||||
@RequestMapping("/sso/*")
|
||||
@PermitAll
|
||||
public Object ssoRequest() {
|
||||
return SaSsoServerProcessor.instance.dister();
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置SSO相关参数
|
||||
*/
|
||||
@Autowired
|
||||
@PermitAll
|
||||
private void configSso(SaSsoServerConfig ssoServer) {
|
||||
// 配置:未登录时返回的View
|
||||
ssoServer.notLoginView = () -> {
|
||||
String msg = "当前会话在SSO-Server端尚未登录,请访问"
|
||||
+ "<a href='/sso/login-page' target='_blank'>登录页面</a>"
|
||||
+ "或 <a href='/sso/register-page' target='_blank'>注册用户</a>"
|
||||
+ ",登录成功后刷新页面开始授权";
|
||||
return msg;
|
||||
};
|
||||
|
||||
// 配置:登录处理函数
|
||||
ssoServer.doLoginHandle = (name, pwd) -> {
|
||||
// 从数据库查询用户
|
||||
SystemUser user = userService.getUserByUsername(name);
|
||||
|
||||
// 验证用户是否存在且密码是否正确
|
||||
if(user != null && userService.validatePassword(pwd, user.getPassword())) {
|
||||
// 用户状态检查
|
||||
if(user.getStatus() != 0) {
|
||||
return SaResult.error("账号已被禁用");
|
||||
}
|
||||
|
||||
// 登录成功,记录用户IP
|
||||
StpUtil.login(user.getId());
|
||||
|
||||
// 获取当前的HTTP请求对象
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
String ip = "unknown";
|
||||
if (attributes != null) {
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
ip = getClientIp(request);
|
||||
}
|
||||
|
||||
userService.updateLoginInfo(user.getId(), ip);
|
||||
|
||||
return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue());
|
||||
}
|
||||
|
||||
return SaResult.error("账号或密码错误");
|
||||
};
|
||||
|
||||
// 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉)
|
||||
ssoServer.sendHttp = url -> {
|
||||
try {
|
||||
System.out.println("------ 发起请求:" + url);
|
||||
String resStr = Forest.get(url).executeAsString();
|
||||
System.out.println("------ 请求结果:" + resStr);
|
||||
return resStr;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端真实IP地址
|
||||
*/
|
||||
private String getClientIp(HttpServletRequest request) {
|
||||
String ip = request.getHeader("X-Forwarded-For");
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
// 多个代理的情况,第一个IP为客户端真实IP
|
||||
if (ip != null && ip.indexOf(",") > 0) {
|
||||
ip = ip.substring(0, ip.indexOf(","));
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单点登录检查 - 自定义接口,方便前端调用
|
||||
*/
|
||||
@GetMapping("/sso/checkLogin")
|
||||
@PermitAll
|
||||
public Object ssoCheckLogin() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("isLogin", StpUtil.isLogin());
|
||||
if (StpUtil.isLogin()) {
|
||||
result.put("loginId", StpUtil.getLoginId());
|
||||
result.put("tokenValue", StpUtil.getTokenValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录页面接口 - 返回登录页面HTML
|
||||
*/
|
||||
@GetMapping("/sso/login-page")
|
||||
@PermitAll
|
||||
public Object loginPage() {
|
||||
String html = "<!DOCTYPE html>\n" +
|
||||
"<html lang=\"zh-CN\">\n" +
|
||||
"<head>\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
|
||||
" <title>单点登录</title>\n" +
|
||||
" <style>\n" +
|
||||
" body {\n" +
|
||||
" font-family: Arial, sans-serif;\n" +
|
||||
" display: flex;\n" +
|
||||
" justify-content: center;\n" +
|
||||
" align-items: center;\n" +
|
||||
" height: 100vh;\n" +
|
||||
" margin: 0;\n" +
|
||||
" background-color: #f5f5f5;\n" +
|
||||
" }\n" +
|
||||
" .login-container {\n" +
|
||||
" background-color: white;\n" +
|
||||
" padding: 30px;\n" +
|
||||
" border-radius: 8px;\n" +
|
||||
" box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n" +
|
||||
" width: 350px;\n" +
|
||||
" }\n" +
|
||||
" h2 {\n" +
|
||||
" text-align: center;\n" +
|
||||
" margin-bottom: 20px;\n" +
|
||||
" }\n" +
|
||||
" .form-group {\n" +
|
||||
" margin-bottom: 15px;\n" +
|
||||
" }\n" +
|
||||
" label {\n" +
|
||||
" display: block;\n" +
|
||||
" margin-bottom: 5px;\n" +
|
||||
" font-weight: bold;\n" +
|
||||
" }\n" +
|
||||
" input {\n" +
|
||||
" width: 100%;\n" +
|
||||
" padding: 10px;\n" +
|
||||
" border: 1px solid #ddd;\n" +
|
||||
" border-radius: 4px;\n" +
|
||||
" box-sizing: border-box;\n" +
|
||||
" }\n" +
|
||||
" button {\n" +
|
||||
" width: 100%;\n" +
|
||||
" padding: 10px;\n" +
|
||||
" background-color: #007bff;\n" +
|
||||
" color: white;\n" +
|
||||
" border: none;\n" +
|
||||
" border-radius: 4px;\n" +
|
||||
" cursor: pointer;\n" +
|
||||
" font-size: 16px;\n" +
|
||||
" }\n" +
|
||||
" button:hover {\n" +
|
||||
" background-color: #0069d9;\n" +
|
||||
" }\n" +
|
||||
" .error-message {\n" +
|
||||
" color: red;\n" +
|
||||
" margin-top: 15px;\n" +
|
||||
" text-align: center;\n" +
|
||||
" display: none;\n" +
|
||||
" }\n" +
|
||||
" .register-link {\n" +
|
||||
" display: block;\n" +
|
||||
" text-align: center;\n" +
|
||||
" margin-top: 20px;\n" +
|
||||
" color: #28a745;\n" +
|
||||
" text-decoration: none;\n" +
|
||||
" }\n" +
|
||||
" .register-link:hover {\n" +
|
||||
" text-decoration: underline;\n" +
|
||||
" }\n" +
|
||||
" </style>\n" +
|
||||
"</head>\n" +
|
||||
"<body>\n" +
|
||||
" <div class=\"login-container\">\n" +
|
||||
" <h2>用户登录</h2>\n" +
|
||||
" <form id=\"loginForm\">\n" +
|
||||
" <div class=\"form-group\">\n" +
|
||||
" <label for=\"username\">用户名</label>\n" +
|
||||
" <input type=\"text\" id=\"username\" name=\"username\" required>\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"form-group\">\n" +
|
||||
" <label for=\"password\">密码</label>\n" +
|
||||
" <input type=\"password\" id=\"password\" name=\"password\" required>\n" +
|
||||
" </div>\n" +
|
||||
" <button type=\"submit\">登录</button>\n" +
|
||||
" </form>\n" +
|
||||
" <div id=\"errorMessage\" class=\"error-message\"></div>\n" +
|
||||
" <a href=\"/sso/register-page\" class=\"register-link\">没有账号?点击注册</a>\n" +
|
||||
" </div>\n" +
|
||||
" <script>\n" +
|
||||
" document.getElementById('loginForm').addEventListener('submit', function(e) {\n" +
|
||||
" e.preventDefault();\n" +
|
||||
" \n" +
|
||||
" const username = document.getElementById('username').value;\n" +
|
||||
" const password = document.getElementById('password').value;\n" +
|
||||
" \n" +
|
||||
" fetch('/sso/login', {\n" +
|
||||
" method: 'POST',\n" +
|
||||
" headers: {\n" +
|
||||
" 'Content-Type': 'application/x-www-form-urlencoded',\n" +
|
||||
" },\n" +
|
||||
" body: 'username=' + encodeURIComponent(username) + '&password=' + encodeURIComponent(password)\n" +
|
||||
" })\n" +
|
||||
" .then(response => response.json())\n" +
|
||||
" .then(data => {\n" +
|
||||
" if (data.code === 200) {\n" +
|
||||
" window.location.href = '/sso/login-success';\n" +
|
||||
" } else {\n" +
|
||||
" const errorMessage = document.getElementById('errorMessage');\n" +
|
||||
" errorMessage.textContent = data.msg || '登录失败';\n" +
|
||||
" errorMessage.style.display = 'block';\n" +
|
||||
" }\n" +
|
||||
" })\n" +
|
||||
" .catch(error => {\n" +
|
||||
" console.error('登录请求失败:', error);\n" +
|
||||
" const errorMessage = document.getElementById('errorMessage');\n" +
|
||||
" errorMessage.textContent = '网络错误,请稍后重试';\n" +
|
||||
" errorMessage.style.display = 'block';\n" +
|
||||
" });\n" +
|
||||
" });\n" +
|
||||
" </script>\n" +
|
||||
"</body>\n" +
|
||||
"</html>";
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录接口 - 前端表单登录请求
|
||||
*/
|
||||
@PostMapping("/sso/login")
|
||||
@PermitAll
|
||||
public Object login(
|
||||
@RequestParam("username") String username,
|
||||
@RequestParam("password") String password,
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 查询用户
|
||||
SystemUser user = userService.getUserByUsername(username);
|
||||
|
||||
// 验证用户
|
||||
if (user == null || !userService.validatePassword(password, user.getPassword())) {
|
||||
return SaResult.error("账号或密码错误");
|
||||
}
|
||||
|
||||
// 检查用户状态
|
||||
if (user.getStatus() != 0) {
|
||||
return SaResult.error("账号已被禁用");
|
||||
}
|
||||
|
||||
// 登录
|
||||
StpUtil.login(user.getId());
|
||||
|
||||
// 更新登录信息
|
||||
String ip = getClientIp(request);
|
||||
userService.updateLoginInfo(user.getId(), ip);
|
||||
|
||||
// 返回结果
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("tokenValue", StpUtil.getTokenValue());
|
||||
result.put("tokenName", StpUtil.getTokenName());
|
||||
|
||||
return SaResult.data(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销登录
|
||||
*/
|
||||
@GetMapping("/sso/logout")
|
||||
@PermitAll
|
||||
public Object logout() {
|
||||
if (StpUtil.isLogin()) {
|
||||
StpUtil.logout();
|
||||
return SaResult.ok("注销成功");
|
||||
}
|
||||
return SaResult.error("当前会话未登录");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户信息 - 自定义接口,用于获取当前登录用户信息
|
||||
*/
|
||||
@GetMapping("/sso/userinfo")
|
||||
@PermitAll
|
||||
public Object ssoUserInfo() {
|
||||
if (!StpUtil.isLogin()) {
|
||||
return SaResult.error("用户未登录");
|
||||
}
|
||||
|
||||
Long userId = Long.valueOf(StpUtil.getLoginId().toString());
|
||||
SystemUser user = userService.getUserById(userId);
|
||||
|
||||
if (user == null) {
|
||||
return SaResult.error("用户不存在");
|
||||
}
|
||||
|
||||
// 获取用户角色信息
|
||||
List<String> roleCodes = userServiceImpl.getUserRoleCodes(userId);
|
||||
List<String> roleNames = userServiceImpl.getUserRoleNames(userId);
|
||||
|
||||
Map<String, Object> userInfo = new HashMap<>();
|
||||
userInfo.put("id", user.getId());
|
||||
userInfo.put("username", user.getUsername());
|
||||
userInfo.put("nickname", user.getNickname());
|
||||
userInfo.put("email", user.getEmail());
|
||||
userInfo.put("mobile", user.getMobile());
|
||||
userInfo.put("avatar", user.getAvatar());
|
||||
userInfo.put("roleCodes", roleCodes);
|
||||
userInfo.put("roleNames", roleNames);
|
||||
|
||||
return SaResult.data(userInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功页面 - 当未指定回调地址时的默认页面
|
||||
*/
|
||||
@GetMapping("/sso/login-success")
|
||||
@PermitAll
|
||||
public Object loginSuccess() {
|
||||
String html = "<!DOCTYPE html>\n" +
|
||||
"<html lang=\"zh-CN\">\n" +
|
||||
"<head>\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
|
||||
" <title>登录成功</title>\n" +
|
||||
" <style>\n" +
|
||||
" body {\n" +
|
||||
" font-family: Arial, sans-serif;\n" +
|
||||
" display: flex;\n" +
|
||||
" justify-content: center;\n" +
|
||||
" align-items: center;\n" +
|
||||
" height: 100vh;\n" +
|
||||
" margin: 0;\n" +
|
||||
" background-color: #f5f5f5;\n" +
|
||||
" flex-direction: column;\n" +
|
||||
" }\n" +
|
||||
" .success-container {\n" +
|
||||
" background-color: white;\n" +
|
||||
" padding: 30px;\n" +
|
||||
" border-radius: 8px;\n" +
|
||||
" box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n" +
|
||||
" width: 400px;\n" +
|
||||
" text-align: center;\n" +
|
||||
" }\n" +
|
||||
" h2 {\n" +
|
||||
" color: #28a745;\n" +
|
||||
" margin-bottom: 20px;\n" +
|
||||
" }\n" +
|
||||
" p {\n" +
|
||||
" margin: 10px 0;\n" +
|
||||
" color: #333;\n" +
|
||||
" }\n" +
|
||||
" .userinfo {\n" +
|
||||
" margin-top: 20px;\n" +
|
||||
" text-align: left;\n" +
|
||||
" padding: 15px;\n" +
|
||||
" background-color: #f8f9fa;\n" +
|
||||
" border-radius: 5px;\n" +
|
||||
" }\n" +
|
||||
" .button {\n" +
|
||||
" display: inline-block;\n" +
|
||||
" margin-top: 20px;\n" +
|
||||
" padding: 10px 20px;\n" +
|
||||
" background-color: #007bff;\n" +
|
||||
" color: white;\n" +
|
||||
" border: none;\n" +
|
||||
" border-radius: 4px;\n" +
|
||||
" text-decoration: none;\n" +
|
||||
" cursor: pointer;\n" +
|
||||
" }\n" +
|
||||
" .button:hover {\n" +
|
||||
" background-color: #0069d9;\n" +
|
||||
" }\n" +
|
||||
" </style>\n" +
|
||||
"</head>\n" +
|
||||
"<body>\n" +
|
||||
" <div class=\"success-container\">\n" +
|
||||
" <h2>登录成功</h2>\n" +
|
||||
" <p>您已成功登录到单点登录系统</p>\n" +
|
||||
" <div class=\"userinfo\" id=\"userinfo\">\n" +
|
||||
" <p>正在加载用户信息...</p>\n" +
|
||||
" </div>\n" +
|
||||
" <a href=\"/sso/logout\" class=\"button\">退出登录</a>\n" +
|
||||
" </div>\n" +
|
||||
" <script>\n" +
|
||||
" // 获取用户信息\n" +
|
||||
" fetch('/sso/userinfo')\n" +
|
||||
" .then(response => response.json())\n" +
|
||||
" .then(data => {\n" +
|
||||
" if (data.code === 200 && data.data) {\n" +
|
||||
" const user = data.data;\n" +
|
||||
" let html = '<p><strong>用户ID:</strong>' + user.id + '</p>';\n" +
|
||||
" html += '<p><strong>用户名:</strong>' + user.username + '</p>';\n" +
|
||||
" html += '<p><strong>昵称:</strong>' + user.nickname + '</p>';\n" +
|
||||
" if (user.email) {\n" +
|
||||
" html += '<p><strong>邮箱:</strong>' + user.email + '</p>';\n" +
|
||||
" }\n" +
|
||||
" if (user.mobile) {\n" +
|
||||
" html += '<p><strong>手机:</strong>' + user.mobile + '</p>';\n" +
|
||||
" }\n" +
|
||||
" if (user.roleNames && user.roleNames.length > 0) {\n" +
|
||||
" html += '<p><strong>角色:</strong>' + user.roleNames.join(', ') + '</p>';\n" +
|
||||
" }\n" +
|
||||
" document.getElementById('userinfo').innerHTML = html;\n" +
|
||||
" } else {\n" +
|
||||
" document.getElementById('userinfo').innerHTML = '<p>无法获取用户信息</p>';\n" +
|
||||
" }\n" +
|
||||
" })\n" +
|
||||
" .catch(error => {\n" +
|
||||
" console.error('获取用户信息失败:', error);\n" +
|
||||
" document.getElementById('userinfo').innerHTML = '<p>获取用户信息时发生错误</p>';\n" +
|
||||
" });\n" +
|
||||
" </script>\n" +
|
||||
"</body>\n" +
|
||||
"</html>";
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册页面 - 返回注册页面HTML
|
||||
*/
|
||||
@GetMapping("/sso/register-page")
|
||||
@PermitAll
|
||||
public Object registerPage() {
|
||||
String html = "<!DOCTYPE html>\n" +
|
||||
"<html lang=\"zh-CN\">\n" +
|
||||
"<head>\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
|
||||
" <title>用户注册</title>\n" +
|
||||
" <style>\n" +
|
||||
" body {\n" +
|
||||
" font-family: Arial, sans-serif;\n" +
|
||||
" display: flex;\n" +
|
||||
" justify-content: center;\n" +
|
||||
" align-items: center;\n" +
|
||||
" height: 100vh;\n" +
|
||||
" margin: 0;\n" +
|
||||
" background-color: #f5f5f5;\n" +
|
||||
" }\n" +
|
||||
" .register-container {\n" +
|
||||
" background-color: white;\n" +
|
||||
" padding: 30px;\n" +
|
||||
" border-radius: 8px;\n" +
|
||||
" box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n" +
|
||||
" width: 400px;\n" +
|
||||
" }\n" +
|
||||
" h2 {\n" +
|
||||
" text-align: center;\n" +
|
||||
" margin-bottom: 20px;\n" +
|
||||
" }\n" +
|
||||
" .form-group {\n" +
|
||||
" margin-bottom: 15px;\n" +
|
||||
" }\n" +
|
||||
" label {\n" +
|
||||
" display: block;\n" +
|
||||
" margin-bottom: 5px;\n" +
|
||||
" font-weight: bold;\n" +
|
||||
" }\n" +
|
||||
" input {\n" +
|
||||
" width: 100%;\n" +
|
||||
" padding: 10px;\n" +
|
||||
" border: 1px solid #ddd;\n" +
|
||||
" border-radius: 4px;\n" +
|
||||
" box-sizing: border-box;\n" +
|
||||
" }\n" +
|
||||
" button {\n" +
|
||||
" width: 100%;\n" +
|
||||
" padding: 10px;\n" +
|
||||
" background-color: #28a745;\n" +
|
||||
" color: white;\n" +
|
||||
" border: none;\n" +
|
||||
" border-radius: 4px;\n" +
|
||||
" cursor: pointer;\n" +
|
||||
" font-size: 16px;\n" +
|
||||
" }\n" +
|
||||
" button:hover {\n" +
|
||||
" background-color: #218838;\n" +
|
||||
" }\n" +
|
||||
" .error-message {\n" +
|
||||
" color: red;\n" +
|
||||
" margin-top: 15px;\n" +
|
||||
" text-align: center;\n" +
|
||||
" display: none;\n" +
|
||||
" }\n" +
|
||||
" .success-message {\n" +
|
||||
" color: green;\n" +
|
||||
" margin-top: 15px;\n" +
|
||||
" text-align: center;\n" +
|
||||
" display: none;\n" +
|
||||
" }\n" +
|
||||
" .login-link {\n" +
|
||||
" display: block;\n" +
|
||||
" text-align: center;\n" +
|
||||
" margin-top: 20px;\n" +
|
||||
" color: #007bff;\n" +
|
||||
" text-decoration: none;\n" +
|
||||
" }\n" +
|
||||
" .login-link:hover {\n" +
|
||||
" text-decoration: underline;\n" +
|
||||
" }\n" +
|
||||
" </style>\n" +
|
||||
"</head>\n" +
|
||||
"<body>\n" +
|
||||
" <div class=\"register-container\">\n" +
|
||||
" <h2>用户注册</h2>\n" +
|
||||
" <form id=\"registerForm\">\n" +
|
||||
" <div class=\"form-group\">\n" +
|
||||
" <label for=\"username\">用户名 *</label>\n" +
|
||||
" <input type=\"text\" id=\"username\" name=\"username\" required>\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"form-group\">\n" +
|
||||
" <label for=\"password\">密码 *</label>\n" +
|
||||
" <input type=\"password\" id=\"password\" name=\"password\" required>\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"form-group\">\n" +
|
||||
" <label for=\"confirmPassword\">确认密码 *</label>\n" +
|
||||
" <input type=\"password\" id=\"confirmPassword\" name=\"confirmPassword\" required>\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"form-group\">\n" +
|
||||
" <label for=\"nickname\">昵称 *</label>\n" +
|
||||
" <input type=\"text\" id=\"nickname\" name=\"nickname\" required>\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"form-group\">\n" +
|
||||
" <label for=\"email\">邮箱</label>\n" +
|
||||
" <input type=\"email\" id=\"email\" name=\"email\">\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"form-group\">\n" +
|
||||
" <label for=\"mobile\">手机号</label>\n" +
|
||||
" <input type=\"tel\" id=\"mobile\" name=\"mobile\">\n" +
|
||||
" </div>\n" +
|
||||
" <button type=\"submit\">注册</button>\n" +
|
||||
" </form>\n" +
|
||||
" <div id=\"errorMessage\" class=\"error-message\"></div>\n" +
|
||||
" <div id=\"successMessage\" class=\"success-message\"></div>\n" +
|
||||
" <a href=\"/sso/login-page\" class=\"login-link\">已有账号?点击登录</a>\n" +
|
||||
" </div>\n" +
|
||||
" <script>\n" +
|
||||
" document.getElementById('registerForm').addEventListener('submit', function(e) {\n" +
|
||||
" e.preventDefault();\n" +
|
||||
" \n" +
|
||||
" const username = document.getElementById('username').value;\n" +
|
||||
" const password = document.getElementById('password').value;\n" +
|
||||
" const confirmPassword = document.getElementById('confirmPassword').value;\n" +
|
||||
" const nickname = document.getElementById('nickname').value;\n" +
|
||||
" const email = document.getElementById('email').value;\n" +
|
||||
" const mobile = document.getElementById('mobile').value;\n" +
|
||||
" \n" +
|
||||
" // 简单前端验证\n" +
|
||||
" if (password !== confirmPassword) {\n" +
|
||||
" const errorMessage = document.getElementById('errorMessage');\n" +
|
||||
" errorMessage.textContent = '两次输入的密码不一致';\n" +
|
||||
" errorMessage.style.display = 'block';\n" +
|
||||
" return;\n" +
|
||||
" }\n" +
|
||||
" \n" +
|
||||
" // 构建表单数据\n" +
|
||||
" const formData = new URLSearchParams();\n" +
|
||||
" formData.append('username', username);\n" +
|
||||
" formData.append('password', password);\n" +
|
||||
" formData.append('nickname', nickname);\n" +
|
||||
" if (email) formData.append('email', email);\n" +
|
||||
" if (mobile) formData.append('mobile', mobile);\n" +
|
||||
" \n" +
|
||||
" // 发送注册请求\n" +
|
||||
" fetch('/user/register', {\n" +
|
||||
" method: 'POST',\n" +
|
||||
" headers: {\n" +
|
||||
" 'Content-Type': 'application/x-www-form-urlencoded',\n" +
|
||||
" },\n" +
|
||||
" body: formData\n" +
|
||||
" })\n" +
|
||||
" .then(response => response.json())\n" +
|
||||
" .then(data => {\n" +
|
||||
" if (data.code === 200) {\n" +
|
||||
" const successMessage = document.getElementById('successMessage');\n" +
|
||||
" successMessage.textContent = '注册成功!3秒后跳转到登录页面...';\n" +
|
||||
" successMessage.style.display = 'block';\n" +
|
||||
" \n" +
|
||||
" // 清空表单\n" +
|
||||
" document.getElementById('registerForm').reset();\n" +
|
||||
" \n" +
|
||||
" // 3秒后跳转到登录页面\n" +
|
||||
" setTimeout(() => {\n" +
|
||||
" window.location.href = '/sso/login-page';\n" +
|
||||
" }, 3000);\n" +
|
||||
" } else {\n" +
|
||||
" const errorMessage = document.getElementById('errorMessage');\n" +
|
||||
" errorMessage.textContent = data.msg || '注册失败,请稍后重试';\n" +
|
||||
" errorMessage.style.display = 'block';\n" +
|
||||
" }\n" +
|
||||
" })\n" +
|
||||
" .catch(error => {\n" +
|
||||
" console.error('注册请求失败:', error);\n" +
|
||||
" const errorMessage = document.getElementById('errorMessage');\n" +
|
||||
" errorMessage.textContent = '网络错误,请稍后重试';\n" +
|
||||
" errorMessage.style.display = 'block';\n" +
|
||||
" });\n" +
|
||||
" });\n" +
|
||||
" </script>\n" +
|
||||
"</body>\n" +
|
||||
"</html>";
|
||||
return html;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package com.tashow.cloud.service;
|
||||
|
||||
import com.tashow.cloud.model.SystemUser;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统用户服务接口
|
||||
*/
|
||||
public interface SystemUserService {
|
||||
|
||||
/**
|
||||
* 根据用户名查询用户
|
||||
*
|
||||
* @param username 用户名
|
||||
* @return 用户信息
|
||||
*/
|
||||
SystemUser getUserByUsername(String username);
|
||||
|
||||
/**
|
||||
* 验证用户密码
|
||||
*
|
||||
* @param rawPassword 原始密码
|
||||
* @param encodedPassword 编码后的密码
|
||||
* @return 是否匹配
|
||||
*/
|
||||
boolean validatePassword(String rawPassword, String encodedPassword);
|
||||
|
||||
/**
|
||||
* 根据用户ID获取用户信息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户信息
|
||||
*/
|
||||
SystemUser getUserById(Long userId);
|
||||
|
||||
/**
|
||||
* 更新用户登录信息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param ip 登录IP
|
||||
*/
|
||||
void updateLoginInfo(Long userId, String ip);
|
||||
|
||||
/**
|
||||
* 注册新用户
|
||||
*
|
||||
* @param user 用户信息
|
||||
* @return 是否注册成功
|
||||
*/
|
||||
boolean registerUser(SystemUser user);
|
||||
|
||||
/**
|
||||
* 加密密码
|
||||
*
|
||||
* @param rawPassword 原始密码
|
||||
* @return 加密后的密码
|
||||
*/
|
||||
String encodePassword(String rawPassword);
|
||||
|
||||
/**
|
||||
* 获取用户角色ID列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 角色ID列表
|
||||
*/
|
||||
List<Long> getUserRoleIds(Long userId);
|
||||
|
||||
/**
|
||||
* 获取用户角色名称列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 角色名称列表
|
||||
*/
|
||||
List<String> getUserRoleNames(Long userId);
|
||||
|
||||
/**
|
||||
* 获取用户角色编码列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 角色编码列表
|
||||
*/
|
||||
List<String> getUserRoleCodes(Long userId);
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
package com.tashow.cloud.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.tashow.cloud.mapper.SystemUserMapper;
|
||||
import com.tashow.cloud.model.SystemRole;
|
||||
import com.tashow.cloud.model.SystemUser;
|
||||
import com.tashow.cloud.model.SystemUserRole;
|
||||
import com.tashow.cloud.service.SystemRoleService;
|
||||
import com.tashow.cloud.service.SystemUserRoleService;
|
||||
import com.tashow.cloud.service.SystemUserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 系统用户服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemUser> implements SystemUserService {
|
||||
|
||||
private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
|
||||
@Autowired
|
||||
private SystemRoleService systemRoleService;
|
||||
|
||||
@Autowired
|
||||
private SystemUserRoleService systemUserRoleService;
|
||||
|
||||
@Override
|
||||
public SystemUser getUserByUsername(String username) {
|
||||
return getOne(new LambdaQueryWrapper<SystemUser>()
|
||||
.eq(SystemUser::getUsername, username)
|
||||
.eq(SystemUser::getStatus, 0)
|
||||
.eq(SystemUser::getDeleted, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validatePassword(String rawPassword, String encodedPassword) {
|
||||
return passwordEncoder.matches(rawPassword, encodedPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SystemUser getUserById(Long userId) {
|
||||
return getOne(new LambdaQueryWrapper<SystemUser>()
|
||||
.eq(SystemUser::getId, userId)
|
||||
.eq(SystemUser::getDeleted, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLoginInfo(Long userId, String ip) {
|
||||
SystemUser user = new SystemUser();
|
||||
user.setId(userId);
|
||||
user.setLoginIp(ip);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
user.setLoginDate(now);
|
||||
user.setUpdateTime(now);
|
||||
updateById(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerUser(SystemUser user) {
|
||||
// 设置默认值
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
user.setCreateTime(now);
|
||||
user.setUpdateTime(now);
|
||||
user.setLoginDate(now);
|
||||
user.setCreator("system");
|
||||
user.setUpdater("system");
|
||||
|
||||
if (user.getAvatar() == null || user.getAvatar().isEmpty()) {
|
||||
user.setAvatar("https://sa-token.cc/logo.png"); // 设置默认头像
|
||||
}
|
||||
|
||||
user.setTenantId(0L); // 默认租户ID
|
||||
user.setDeleted(false); // 设置为未删除
|
||||
|
||||
return save(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodePassword(String rawPassword) {
|
||||
return passwordEncoder.encode(rawPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色ID列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 角色ID列表
|
||||
*/
|
||||
@Override
|
||||
public List<Long> getUserRoleIds(Long userId) {
|
||||
List<SystemUserRole> userRoles = systemUserRoleService.list(new LambdaQueryWrapper<SystemUserRole>()
|
||||
.eq(SystemUserRole::getUserId, userId)
|
||||
.eq(SystemUserRole::getDeleted, false));
|
||||
|
||||
return userRoles.stream()
|
||||
.map(SystemUserRole::getRoleId)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色名称列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 角色名称列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getUserRoleNames(Long userId) {
|
||||
// 先获取用户角色ID列表
|
||||
List<Long> roleIds = getUserRoleIds(userId);
|
||||
if (roleIds.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 再查询角色名称
|
||||
List<SystemRole> roles = systemRoleService.list(new LambdaQueryWrapper<SystemRole>()
|
||||
.in(SystemRole::getId, roleIds)
|
||||
.eq(SystemRole::getStatus, 0)
|
||||
.eq(SystemRole::getDeleted, false));
|
||||
|
||||
return roles.stream()
|
||||
.map(SystemRole::getName)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色编码列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 角色编码列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getUserRoleCodes(Long userId) {
|
||||
// 先获取用户角色ID列表
|
||||
List<Long> roleIds = getUserRoleIds(userId);
|
||||
if (roleIds.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 再查询角色编码
|
||||
List<SystemRole> roles = systemRoleService.list(new LambdaQueryWrapper<SystemRole>()
|
||||
.in(SystemRole::getId, roleIds)
|
||||
.eq(SystemRole::getStatus, 0)
|
||||
.eq(SystemRole::getDeleted, false));
|
||||
|
||||
return roles.stream()
|
||||
.map(SystemRole::getCode)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.tashow.cloud.config;
|
||||
package com.tashow.cloud.sso.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.tashow.cloud.sso.controller;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
|
||||
import cn.dev33.satoken.sso.template.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import com.tashow.cloud.sso.model.SystemUser;
|
||||
import com.tashow.cloud.sso.service.SystemUserService;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
/**
|
||||
* Sa-Token-SSO Server端 Controller
|
||||
*/
|
||||
@RestController
|
||||
public class SsoServerController {
|
||||
@Autowired
|
||||
private SystemUserService systemUserService;
|
||||
/**
|
||||
* SSO-Server端:处理所有SSO相关请求 (下面的章节我们会详细列出开放的接口)
|
||||
*/
|
||||
@RequestMapping("sso/*")
|
||||
@PermitAll
|
||||
public Object ssoRequest() {
|
||||
return SaSsoServerProcessor.instance.dister();
|
||||
}
|
||||
|
||||
// 测试登录,浏览器访问: http://localhost:48083/doLogin?username=zhang&password=123456
|
||||
@RequestMapping("doLogin")
|
||||
@PermitAll
|
||||
public String doLogin(String username, String password) {
|
||||
// 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
|
||||
if("zhang".equals(username) && "123456".equals(password)) {
|
||||
StpUtil.login(10001);
|
||||
return "登录成功";
|
||||
}
|
||||
return "登录失败";
|
||||
}
|
||||
// 返回SSO认证中心登录地址
|
||||
@PermitAll
|
||||
@RequestMapping("sso/getSsoAuthUrl")
|
||||
public SaResult getSsoAuthUrl(String clientLoginUrl) {
|
||||
String serverAuthUrl = SaSsoUtil.buildServerAuthUrl(clientLoginUrl, "");
|
||||
return SaResult.data(serverAuthUrl);
|
||||
}
|
||||
|
||||
|
||||
// 查询登录状态,浏览器访问: http://localhost:48083/isLogin
|
||||
@RequestMapping("isLogin")
|
||||
@PermitAll
|
||||
public String isLogin() {
|
||||
return "当前会话是否登录:" + StpUtil.isLogin();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
package com.tashow.cloud.framework.core;
|
||||
|
||||
package com.tashow.cloud.sso.framework.core;
|
||||
import com.xingyuv.captcha.service.CaptchaCacheService;
|
||||
import lombok.Setter;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
@@ -21,6 +20,7 @@ public class RedisCaptchaServiceImpl implements CaptchaCacheService {
|
||||
return "redis";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void set(String key, String value, long expiresInSeconds) {
|
||||
stringRedisTemplate.opsForValue().set(key, value, expiresInSeconds, TimeUnit.SECONDS);
|
||||
@@ -1,8 +1,9 @@
|
||||
package com.tashow.cloud.mapper;
|
||||
package com.tashow.cloud.sso.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.tashow.cloud.model.SystemRole;
|
||||
import com.tashow.cloud.sso.model.SystemRole;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 系统角色Mapper接口
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.tashow.cloud.mapper;
|
||||
package com.tashow.cloud.sso.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.tashow.cloud.model.SystemUser;
|
||||
import com.tashow.cloud.sso.model.SystemUser;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.tashow.cloud.mapper;
|
||||
package com.tashow.cloud.sso.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.tashow.cloud.model.SystemUserRole;
|
||||
import com.tashow.cloud.sso.model.SystemUserRole;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.tashow.cloud.model;
|
||||
package com.tashow.cloud.sso.model;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.tashow.cloud.model;
|
||||
package com.tashow.cloud.sso.model;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@@ -7,6 +8,7 @@ import java.time.LocalDateTime;
|
||||
* 系统用户实体类
|
||||
*/
|
||||
@Data
|
||||
@TableName("system_users")
|
||||
public class SystemUser {
|
||||
/**
|
||||
* 用户ID
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.tashow.cloud.model;
|
||||
package com.tashow.cloud.sso.model;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.tashow.cloud.security.config;
|
||||
package com.tashow.cloud.sso.security.config;
|
||||
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.tashow.cloud.security.config;
|
||||
package com.tashow.cloud.sso.security.config;
|
||||
|
||||
import com.tashow.cloud.security.security.config.AuthorizeRequestsCustomizer;
|
||||
import com.tashow.cloud.systemapi.enums.ApiConstants;
|
||||
@@ -25,6 +25,8 @@ public class SecurityConfiguration {
|
||||
.requestMatchers("/webjars/**").permitAll()
|
||||
.requestMatchers("/swagger-ui").permitAll()
|
||||
.requestMatchers("/swagger-ui/**").permitAll()
|
||||
.requestMatchers("/oauth2/**").permitAll()
|
||||
.requestMatchers("/sso/**").permitAll()
|
||||
;
|
||||
// Druid 监控
|
||||
registry.requestMatchers("/druid/**").permitAll();
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.tashow.cloud.service;
|
||||
package com.tashow.cloud.sso.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.tashow.cloud.model.SystemRole;
|
||||
import com.tashow.cloud.sso.model.SystemRole;
|
||||
|
||||
/**
|
||||
* 系统角色服务接口
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.tashow.cloud.service;
|
||||
package com.tashow.cloud.sso.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.tashow.cloud.model.SystemUserRole;
|
||||
import com.tashow.cloud.sso.model.SystemUserRole;
|
||||
|
||||
/**
|
||||
* 系统用户角色关联服务接口
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.tashow.cloud.sso.service;
|
||||
|
||||
import com.tashow.cloud.sso.model.SystemUser;
|
||||
|
||||
/**
|
||||
* 系统用户服务接口
|
||||
*/
|
||||
public interface SystemUserService {
|
||||
|
||||
/**
|
||||
* 根据用户名查询用户
|
||||
*
|
||||
* @param username 用户名
|
||||
* @return 用户信息
|
||||
*/
|
||||
SystemUser getUserByUsername(String username);
|
||||
|
||||
/**
|
||||
* 验证用户密码
|
||||
*
|
||||
* @param rawPassword 原始密码
|
||||
* @param encodedPassword 编码后的密码
|
||||
* @return 是否匹配
|
||||
*/
|
||||
boolean validatePassword(String rawPassword, String encodedPassword);
|
||||
|
||||
|
||||
/**
|
||||
* 更新用户登录信息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param ip 登录IP
|
||||
*/
|
||||
void updateLoginInfo(Long userId, String ip);
|
||||
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.tashow.cloud.service.impl;
|
||||
package com.tashow.cloud.sso.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.tashow.cloud.mapper.SystemRoleMapper;
|
||||
import com.tashow.cloud.model.SystemRole;
|
||||
import com.tashow.cloud.service.SystemRoleService;
|
||||
import com.tashow.cloud.sso.mapper.SystemRoleMapper;
|
||||
import com.tashow.cloud.sso.model.SystemRole;
|
||||
import com.tashow.cloud.sso.service.SystemRoleService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.tashow.cloud.service.impl;
|
||||
package com.tashow.cloud.sso.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.tashow.cloud.mapper.SystemUserRoleMapper;
|
||||
import com.tashow.cloud.model.SystemUserRole;
|
||||
import com.tashow.cloud.service.SystemUserRoleService;
|
||||
import com.tashow.cloud.sso.mapper.SystemUserRoleMapper;
|
||||
import com.tashow.cloud.sso.model.SystemUserRole;
|
||||
import com.tashow.cloud.sso.service.SystemUserRoleService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.tashow.cloud.sso.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.tashow.cloud.sso.mapper.SystemUserMapper;
|
||||
import com.tashow.cloud.sso.model.SystemUser;
|
||||
import com.tashow.cloud.sso.service.SystemUserService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 系统用户服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemUser> implements SystemUserService {
|
||||
|
||||
private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
|
||||
|
||||
@Override
|
||||
public SystemUser getUserByUsername(String username) {
|
||||
return getOne(new LambdaQueryWrapper<SystemUser>()
|
||||
.eq(SystemUser::getUsername, username)
|
||||
.eq(SystemUser::getStatus, 0)
|
||||
.eq(SystemUser::getDeleted, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validatePassword(String rawPassword, String encodedPassword) {
|
||||
return passwordEncoder.matches(rawPassword, encodedPassword);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateLoginInfo(Long userId, String ip) {
|
||||
SystemUser user = new SystemUser();
|
||||
user.setId(userId);
|
||||
user.setLoginIp(ip);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
user.setLoginDate(now);
|
||||
user.setUpdateTime(now);
|
||||
updateById(user);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
com.tashow.cloud.framework.core.RedisCaptchaServiceImpl
|
||||
com.tashow.cloud.sso.framework.core.RedisCaptchaServiceImpl
|
||||
@@ -137,7 +137,7 @@ aj:
|
||||
tashow:
|
||||
info:
|
||||
version: 1.0.0
|
||||
base-package: com.tashow.cloud.system
|
||||
base-package: com.tashow.cloud
|
||||
web:
|
||||
admin-ui:
|
||||
url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址
|
||||
@@ -161,6 +161,8 @@ tashow:
|
||||
- /rpc-api/system/tenant/valid # 防止递归。避免调用 /rpc-api/system/tenant/valid 接口时,又去触发 /rpc-api/system/tenant/valid 去校验
|
||||
- /rpc-api/system/tenant/id-list # 获得租户列表的时候,无需传递租户编号
|
||||
- /rpc-api/system/oauth2/token/check # 访问令牌校验时,无需传递租户编号;主要解决上传文件的场景,前端不会传递 tenant-id!
|
||||
# - /oauth2/* # OAuth2相关接口,无需传递租户编号
|
||||
# - /oauth2/login # OAuth2登录接口,无需传递租户编号
|
||||
ignore-tables:
|
||||
- system_tenant
|
||||
- system_tenant_package
|
||||
@@ -196,21 +198,20 @@ debug: false
|
||||
|
||||
|
||||
sa-token:
|
||||
# ------- SSO-模式一相关配置 (非模式一不需要配置)
|
||||
# cookie:
|
||||
# 配置 Cookie 作用域
|
||||
# domain: stp.com
|
||||
|
||||
# ------- SSO-模式二相关配置
|
||||
sso-server:
|
||||
# Ticket有效期 (单位: 秒),默认五分钟
|
||||
ticket-timeout: 300
|
||||
# 所有允许的授权回调地址
|
||||
allow-url: "*"
|
||||
home-route: "/sso/login-success"
|
||||
# ------- SSO-模式三相关配置 (下面的配置在使用SSO模式三时打开)
|
||||
# 是否打开模式三
|
||||
is-http: true
|
||||
# token 名称(同时也是 cookie 名称)
|
||||
token-name: token
|
||||
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
||||
timeout: 2592000
|
||||
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
||||
active-timeout: -1
|
||||
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
|
||||
is-concurrent: true
|
||||
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
|
||||
is-share: false
|
||||
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
||||
token-style: uuid
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
# OAuth2相关配置
|
||||
oauth2:
|
||||
|
||||
Reference in New Issue
Block a user