sso service

This commit is contained in:
Github_lizhw 2024-10-21 23:07:11 +08:00
parent 88cb7887e1
commit 2f3e873980
20 changed files with 376 additions and 183 deletions

View File

@ -16,6 +16,42 @@
</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Nacos注册中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--nacos配置中心 排除了不是他的问题-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- 服务发现OpenFeign服务调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 没有loadbalancer调用会报错 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>logback-adapter</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Spring Cloud Starter 排除了不是他的问题 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- 后续上面的pom换位置-->
<!-- Mysql驱动包 -->
<dependency>

View File

@ -1,4 +1,4 @@
package org.dromara.common.satoken.config;
package org.dromara.web.config;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;

View File

@ -92,6 +92,8 @@ sa-token:
is-share: false
# jwt秘钥
jwt-secret-key: abcdefghijklmnopqrstuvwxyz
# token前缀
token-prefix: "Bearer"
# security配置
security:

View File

@ -40,7 +40,16 @@
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<version>1.38.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -4,10 +4,8 @@ sa-token:
# 允许动态设置 token 有效期
dynamic-active-timeout: true
# 允许从 请求参数 读取 token
is-read-body: true
is-read-body: false
# 允许从 header 读取 token
is-read-header: true
# 关闭 cookie 鉴权 从根源杜绝 csrf 漏洞风险
is-read-cookie: false
# token前缀
token-prefix: "Bearer"

View File

@ -87,6 +87,6 @@
<version>1.38.0</version>
</dependency>
</dependencies>
</project>
</project>

View File

@ -17,5 +17,5 @@ public class SaSsoServerApplication {
System.out.println("测试前需要根据官网文档修改hosts文件测试账号密码sa / 123456");
System.out.println();
}
}
}

View File

@ -0,0 +1,47 @@
package com.pj.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 跨域过滤器
*
* @author lizhw
*/
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600)
.exposedHeaders("Authorization");
}
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.setAllowCredentials(true);
config.setMaxAge(3600L);
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}

View File

@ -1,4 +1,4 @@
package com.pj.sso;
package com.pj.config;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@ -6,18 +6,18 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
import cn.dev33.satoken.util.SaResult;
/**
* 全局异常处理
* 全局异常处理
* @author click33
*
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
// 全局异常拦截
// 全局异常拦截
@ExceptionHandler
public SaResult handlerException(Exception e) {
e.printStackTrace();
e.printStackTrace();
return SaResult.error(e.getMessage());
}
}

View File

@ -0,0 +1,44 @@
package com.pj.config;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpLogic;
import org.dromara.common.core.factory.YmlPropertySourceFactory;
import org.dromara.common.satoken.core.service.SaPermissionImpl;
import org.dromara.common.satoken.handler.SaTokenExceptionHandler;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
/**
* sa-token 配置
*
* @author Lion Li
*/
@AutoConfiguration
@PropertySource(value = "classpath:common-satoken.yml", factory = YmlPropertySourceFactory.class)
public class SaTokenConfig {
@Bean
public StpLogic getStpLogicJwt() {
// Sa-Token 整合 jwt (简单模式)
return new StpLogicJwtForSimple();
}
/**
* 权限接口实现(使用bean注入方便用户替换)
*/
@Bean
public StpInterface stpInterface() {
return new SaPermissionImpl();
}
/**
* 异常处理器
*/
@Bean
public SaTokenExceptionHandler saTokenExceptionHandler() {
return new SaTokenExceptionHandler();
}
}

View File

@ -1,4 +1,4 @@
package com.pj.home;
package com.pj.controller;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.stp.StpUtil;

View File

@ -1,13 +1,9 @@
package com.pj.sso;
package com.pj.controller;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.sign.SaSignUtil;
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 cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.pj.model.vo.LoginVo;
import com.pj.model.vo.SysClientVo;
@ -35,16 +31,6 @@ public class SsoServerController {
*/
@RequestMapping("/sso/*")
public Object ssoRequest() {
// 如果登录时没有提供redirect参数则进入平台中心首页 /home而不是重定向到 client
SaRequest req = SaHolder.getRequest();
if(req.isPath("/sso/auth") && !req.hasParam("redirect") && StpUtil.isLogin()) {
return SaHolder.getResponse().redirect("/home");
}
if(req.isPath("/sso/isLogin")) {
return SaResult.data(StpUtil.isLogin());
}
return SaSsoServerProcessor.instance.dister();
}
@ -61,7 +47,6 @@ public class SsoServerController {
ssoServer.doLoginHandle = (name, pwd) -> {
// 此处仅做模拟登录真实环境应该查询数据进行登录
if("sa".equals(name) && "123456".equals(pwd)) {
StpUtil.login(10001);
PasswordLoginBody body = new PasswordLoginBody();
body.setUsername("sa");
body.setPassword("123456");
@ -71,12 +56,16 @@ public class SsoServerController {
body.setCode("code1");
body.setUuid("qweqw");
SysClientVo client = new SysClientVo();
client.setId(1L);
client.setTimeout(10000L);
client.setActiveTimeout(1000000L);
client.setClientId("ClientId1");
client.setClientKey("setClientKey");
client.setClientSecret("setClientSecret");
client.setGrantType("password");
client.setDeviceType("pc");
client.setActiveTimeout(604800L);
client.setTimeout(604800L);
client.setStatus("0");
LoginVo loginVo = IAuthStrategy.login(JSONUtil.toJsonStr(body), client, body.getGrantType());
@ -87,22 +76,4 @@ public class SsoServerController {
}
// 示例获取数据接口用于在模式三下 client 端开放拉取数据的接口
@RequestMapping("/sso/getData")
public SaResult getData(String apiType, String loginId) {
System.out.println("---------------- 获取数据 ----------------");
System.out.println("apiType=" + apiType);
System.out.println("loginId=" + loginId);
// 校验签名只有拥有正确秘钥发起的请求才能通过校验
SaSignUtil.checkRequest(SaHolder.getRequest());
// 自定义返回结果模拟
return SaResult.ok()
.set("id", loginId)
.set("name", "LinXiaoYu")
.set("sex", "")
.set("age", 18);
}
}

View File

@ -1,44 +0,0 @@
package com.pj.h5;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.sso.util.SaSsoConsts;
import cn.dev33.satoken.sso.template.SaSsoUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaResult;
/**
* 前后台分离架构下集成SSO所需的代码 SSO-Server端
* <p>如果不需要前后端分离架构下集成SSO可删除此包下所有代码</p>
* @author click33
*
*/
@RestController
public class H5Controller {
/**
* 获取 redirectUrl
*/
@RequestMapping("/sso/getRedirectUrl")
public SaResult getRedirectUrl(String redirect, String mode, String client) {
// 未登录情况下返回 code=401
if(!StpUtil.isLogin()) {
return SaResult.code(401);
}
// 已登录情况下构建 redirectUrl
redirect = SaFoxUtil.decoderUrl(redirect);
if(SaSsoConsts.MODE_SIMPLE.equals(mode)) {
// 模式一
SaSsoUtil.checkRedirectUrl(redirect);
return SaResult.data(redirect);
} else {
// 模式二或模式三
String redirectUrl = SaSsoUtil.buildRedirectUrl(StpUtil.getLoginId(), client, redirect);
return SaResult.data(redirectUrl);
}
}
}

View File

@ -0,0 +1,202 @@
//package com.pj.utils;
//
//import cn.dev33.satoken.session.SaSession;
//import cn.dev33.satoken.stp.SaLoginModel;
//import cn.dev33.satoken.stp.StpUtil;
//import cn.hutool.core.collection.CollUtil;
//import cn.hutool.core.convert.Convert;
//import cn.hutool.core.util.ObjectUtil;
//import lombok.AccessLevel;
//import lombok.NoArgsConstructor;
//import org.dromara.common.core.constant.TenantConstants;
//import org.dromara.common.core.constant.UserConstants;
//import org.dromara.common.core.domain.model.LoginUser;
//import org.dromara.common.core.enums.UserType;
//
//import java.util.Set;
//
///**
// * 登录鉴权助手
// * <p>
// * user_type 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app
// * deivce 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios
// * 可以组成 用户类型与设备类型多对多的 权限灵活控制
// * <p>
// * 多用户体系 针对 多种用户类型 但权限控制不一致
// * 可以组成 多用户类型表与多设备类型 分别控制权限
// *
// * @author Lion Li
// */
//@NoArgsConstructor(access = AccessLevel.PRIVATE)
//public class LoginHelper {
//
// public static final String LOGIN_USER_KEY = "loginUser";
// public static final String TENANT_KEY = "tenantId";
// public static final String USER_KEY = "userId";
// public static final String USER_NAME_KEY = "userName";
// public static final String DEPT_KEY = "deptId";
// public static final String DEPT_NAME_KEY = "deptName";
// public static final String DEPT_CATEGORY_KEY = "deptCategory";
// public static final String CLIENT_KEY = "clientid";
//
// /**
// * 登录系统 基于 设备类型
// * 针对相同用户体系不同设备
// *
// * @param loginUser 登录用户信息
// * @param model 配置参数
// */
// public static void login(LoginUser loginUser, SaLoginModel model) {
// model = ObjectUtil.defaultIfNull(model, new SaLoginModel());
// StpUtil.login(loginUser.getLoginId(),
// model.setExtra(TENANT_KEY, loginUser.getTenantId())
// .setExtra(USER_KEY, loginUser.getUserId())
// .setExtra(USER_NAME_KEY, loginUser.getUsername())
// .setExtra(DEPT_KEY, loginUser.getDeptId())
// .setExtra(DEPT_NAME_KEY, loginUser.getDeptName())
// .setExtra(DEPT_CATEGORY_KEY, loginUser.getDeptCategory())
// );
// StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
// }
//
// /**
// * 获取用户(多级缓存)
// */
// public static LoginUser getLoginUser() {
// SaSession session = StpUtil.getTokenSession();
// if (ObjectUtil.isNull(session)) {
// return null;
// }
// return (LoginUser) session.get(LOGIN_USER_KEY);
// }
//
// /**
// * 获取用户基于token
// */
// public static LoginUser getLoginUser(String token) {
// SaSession session = StpUtil.getTokenSessionByToken(token);
// if (ObjectUtil.isNull(session)) {
// return null;
// }
// return (LoginUser) session.get(LOGIN_USER_KEY);
// }
//
// /**
// * 获取用户id
// */
// public static Long getUserId() {
// return Convert.toLong(getExtra(USER_KEY));
// }
//
// /**
// * 获取用户账户
// */
// public static String getUsername() {
// return Convert.toStr(getExtra(USER_NAME_KEY));
// }
//
// /**
// * 获取租户ID
// */
// public static String getTenantId() {
// return Convert.toStr(getExtra(TENANT_KEY));
// }
//
// /**
// * 获取部门ID
// */
// public static Long getDeptId() {
// return Convert.toLong(getExtra(DEPT_KEY));
// }
//
// /**
// * 获取部门名
// */
// public static String getDeptName() {
// return Convert.toStr(getExtra(DEPT_NAME_KEY));
// }
//
// /**
// * 获取部门类别编码
// */
// public static String getDeptCategory() {
// return Convert.toStr(getExtra(DEPT_CATEGORY_KEY));
// }
//
// /**
// * 获取当前 Token 的扩展信息
// *
// * @param key 键值
// * @return 对应的扩展数据
// */
// private static Object getExtra(String key) {
// try {
// return StpUtil.getExtra(key);
// } catch (Exception e) {
// return null;
// }
// }
//
// /**
// * 获取用户类型
// */
// public static UserType getUserType() {
// String loginType = StpUtil.getLoginIdAsString();
// return UserType.getUserType(loginType);
// }
//
// /**
// * 是否为超级管理员
// *
// * @param userId 用户ID
// * @return 结果
// */
// public static boolean isSuperAdmin(Long userId) {
// return UserConstants.SUPER_ADMIN_ID.equals(userId);
// }
//
// /**
// * 是否为超级管理员
// *
// * @return 结果
// */
// public static boolean isSuperAdmin() {
// return isSuperAdmin(getUserId());
// }
//
// /**
// * 是否为租户管理员
// *
// * @param rolePermission 角色权限标识组
// * @return 结果
// */
// public static boolean isTenantAdmin(Set<String> rolePermission) {
// if (CollUtil.isEmpty(rolePermission)) {
// return false;
// }
// return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY);
// }
//
// /**
// * 是否为租户管理员
// *
// * @return 结果
// */
// public static boolean isTenantAdmin() {
// return Convert.toBool(isTenantAdmin(getLoginUser().getRolePermission()));
// }
//
// /**
// * 检查当前用户是否已登录
// *
// * @return 结果
// */
// public static boolean isLogin() {
// try {
// return getLoginUser() != null;
// } catch (Exception e) {
// return false;
// }
// }
//
//}

View File

@ -77,7 +77,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
loginUser.setDeptId(2L);
loginUser.setUsername("测试用户登录名");
loginUser.setNickname("测试用户名称");
loginUser.setUserType("用户类型1");
loginUser.setUserType("app_user");
return loginUser;
}

View File

@ -1,6 +1,6 @@
# 端口
server:
port: 9002
port: 9000
spring:
cloud:
@ -42,6 +42,8 @@ spring:
sa-token:
# token名称 (同时也是cookie名称)
token-name: Authorization
# cookie写入
is-read-cookie: true
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
@ -74,4 +76,3 @@ security:
- /*/api-docs
- /*/api-docs/**

View File

@ -1,15 +1,11 @@
// sa
// sa
var sa = {};
sa.setToken = function (token) {
localStorage.setItem("Authorization", token)
};
sa.getToken = function () {
return "Bearer " + localStorage.getItem("Authorization")
};
$.ajaxSettings.beforeSend = function (xhr, request) {
xhr.setRequestHeader("Authorization", sa.getToken());
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
// 打开loading
@ -24,28 +20,6 @@ sa.hideLoading = function () {
};
$.ajax({
url: "sso/isLogin",
type: "get",
success: function (res) {
debugger
console.log('返回数据:', res);
sa.hideLoading();
if (res.code === 200 && res.data) {
layer.msg('登录成功', {anim: 0, icon: 6});
} else {
layer.msg("检验未登录!", {anim: 6, icon: 2});
}
},
error: function (xhr, type, errorThrown) {
sa.hideLoading();
if (xhr.status == 0) {
return layer.alert('无法连接到服务器,请检查网络');
}
return layer.alert("异常:" + JSON.stringify(xhr));
}
});
// ----------------------------------- 登录事件 -----------------------------------
$('.login-btn').click(function () {
@ -63,9 +37,9 @@ $('.login-btn').click(function () {
success: function (res) {
console.log('返回数据:', res);
sa.hideLoading();
if (res.code === 200) {
if (res.code == 200) {
layer.msg('登录成功', {anim: 0, icon: 6});
sa.setToken(res.data);
setCookie("Authorization", "Bearer " + res.data, 1);
setTimeout(function () {
location.reload();
}, 800)
@ -94,6 +68,6 @@ $('[name=name],[name=pwd]').bind('keypress', function (event) {
// 输入框获取焦点
$("[name=name]").focus();
// 打印信息
// 打印信息
var str = "This page is provided by Sa-Token, Please refer to: " + "https://sa-token.cc/";
console.log(str);

View File

@ -16,29 +16,6 @@
</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Nacos注册中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--nacos配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Spring Cloud Starter -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>org.dromara</groupId>

View File

@ -16,29 +16,6 @@
</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Nacos注册中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--nacos配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Spring Cloud Starter -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>org.dromara</groupId>