重构SSO模块,抽离三种模式的统一认证中心
@@ -13,6 +13,7 @@ import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.annotation.SaCheckSafe;
|
||||
import cn.dev33.satoken.basic.SaBasicUtil;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
|
||||
@@ -115,34 +116,35 @@ public class SaTokenActionDefaultImpl implements SaTokenAction {
|
||||
public void validateAnnotation(AnnotatedElement target) {
|
||||
|
||||
// 校验 @SaCheckLogin 注解
|
||||
if(target.isAnnotationPresent(SaCheckLogin.class)) {
|
||||
SaCheckLogin at = target.getAnnotation(SaCheckLogin.class);
|
||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||
SaCheckLogin checkLogin = (SaCheckLogin) SaStrategy.me.getAnnotation.apply(target, SaCheckLogin.class);
|
||||
if(checkLogin != null) {
|
||||
SaManager.getStpLogic(checkLogin.type()).checkByAnnotation(checkLogin);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckRole 注解
|
||||
if(target.isAnnotationPresent(SaCheckRole.class)) {
|
||||
SaCheckRole at = target.getAnnotation(SaCheckRole.class);
|
||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||
SaCheckRole checkRole = (SaCheckRole) SaStrategy.me.getAnnotation.apply(target, SaCheckRole.class);
|
||||
if(checkRole != null) {
|
||||
SaManager.getStpLogic(checkRole.type()).checkByAnnotation(checkRole);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckPermission 注解
|
||||
if(target.isAnnotationPresent(SaCheckPermission.class)) {
|
||||
SaCheckPermission at = target.getAnnotation(SaCheckPermission.class);
|
||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||
SaCheckPermission checkPermission = (SaCheckPermission) SaStrategy.me.getAnnotation.apply(target, SaCheckPermission.class);
|
||||
if(checkPermission != null) {
|
||||
SaManager.getStpLogic(checkPermission.type()).checkByAnnotation(checkPermission);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckSafe 注解
|
||||
if(target.isAnnotationPresent(SaCheckSafe.class)) {
|
||||
SaCheckSafe at = target.getAnnotation(SaCheckSafe.class);
|
||||
SaManager.getStpLogic(null).checkByAnnotation(at);
|
||||
SaCheckSafe checkSafe = (SaCheckSafe) SaStrategy.me.getAnnotation.apply(target, SaCheckSafe.class);
|
||||
if(checkSafe != null) {
|
||||
SaManager.getStpLogic(checkSafe.type()).checkByAnnotation(checkSafe);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckBasic 注解
|
||||
if(target.isAnnotationPresent(SaCheckBasic.class)) {
|
||||
SaCheckBasic at = target.getAnnotation(SaCheckBasic.class);
|
||||
SaBasicUtil.check(at.realm(), at.account());
|
||||
}
|
||||
SaCheckBasic checkBasic = (SaCheckBasic) SaStrategy.me.getAnnotation.apply(target, SaCheckBasic.class);
|
||||
if(checkBasic != null) {
|
||||
SaBasicUtil.check(checkBasic.realm(), checkBasic.account());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -15,4 +15,10 @@ import java.lang.annotation.Target;
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
public @interface SaCheckSafe {
|
||||
|
||||
/**
|
||||
* 多账号体系下所属的账号体系标识
|
||||
* @return see note
|
||||
*/
|
||||
String type() default "";
|
||||
|
||||
}
|
||||
|
@@ -48,6 +48,9 @@ public class SaSsoConsts {
|
||||
/** back参数名称 */
|
||||
public static String back = "back";
|
||||
|
||||
/** mode参数名称 */
|
||||
public static String mode = "mode";
|
||||
|
||||
/** loginId参数名称 */
|
||||
public static String loginId = "loginId";
|
||||
|
||||
@@ -71,6 +74,12 @@ public class SaSsoConsts {
|
||||
/** 表示自己 */
|
||||
public static final String SELF = "self";
|
||||
|
||||
/** 表示简单模式(SSO模式一) */
|
||||
public static final String MODE_SIMPLE = "simple";
|
||||
|
||||
/** 表示ticket模式(SSO模式二和模式三) */
|
||||
public static final String MODE_TICKET = "ticket";
|
||||
|
||||
/** 表示请求没有得到任何有效处理 {msg: "not handle"} */
|
||||
public static final String NOT_HANDLE = "{\"msg\": \"not handle\"}";
|
||||
|
||||
|
@@ -20,7 +20,7 @@ import cn.dev33.satoken.util.SaResult;
|
||||
public class SaSsoHandle {
|
||||
|
||||
/**
|
||||
* 处理所有Server端请求
|
||||
* 处理Server端所有请求
|
||||
* @return 处理结果
|
||||
*/
|
||||
public static Object serverRequest() {
|
||||
@@ -46,8 +46,13 @@ public class SaSsoHandle {
|
||||
return ssoCheckTicket();
|
||||
}
|
||||
|
||||
// SSO-Server端:单点注销
|
||||
if(req.isPath(Api.ssoLogout) && cfg.isSlo) {
|
||||
// SSO-Server端:单点注销 [模式一] (不带loginId参数)
|
||||
if(req.isPath(Api.ssoLogout) && cfg.isSlo && req.hasParam(ParamName.loginId) == false) {
|
||||
return ssoServerLogoutType1();
|
||||
}
|
||||
|
||||
// SSO-Server端:单点注销 [模式三] (带loginId参数)
|
||||
if(req.isPath(Api.ssoLogout) && cfg.isHttp && cfg.isSlo && req.hasParam(ParamName.loginId)) {
|
||||
return ssoServerLogout();
|
||||
}
|
||||
|
||||
@@ -66,15 +71,25 @@ public class SaSsoHandle {
|
||||
SaSsoConfig cfg = SaManager.getConfig().getSso();
|
||||
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
||||
|
||||
// ---------- 此处两种情况分开处理:
|
||||
// 情况1:在SSO认证中心尚未登录,则先去登登录
|
||||
// ---------- 此处有两种情况分开处理:
|
||||
// ---- 情况1:在SSO认证中心尚未登录,需要先去登录
|
||||
if(stpLogic.isLogin() == false) {
|
||||
return cfg.notLoginView.get();
|
||||
}
|
||||
// 情况2:在SSO认证中心已经登录,开始构建授权重定向地址,下放ticket
|
||||
// ---- 情况2:在SSO认证中心已经登录,需要重定向回 Client 端,而这又分为两种方式:
|
||||
String mode = req.getParam(ParamName.mode, "");
|
||||
|
||||
// 方式1:直接重定向回Client端 (mode=simple)
|
||||
if(mode.equals(SaSsoConsts.MODE_SIMPLE)) {
|
||||
String redirect = req.getParam(ParamName.redirect);
|
||||
SaSsoUtil.checkRedirectUrl(redirect);
|
||||
return res.redirect(redirect);
|
||||
} else {
|
||||
// 方式2:带着ticket参数重定向回Client端 (mode=ticket)
|
||||
String redirectUrl = SaSsoUtil.buildRedirectUrl(stpLogic.getLoginId(), req.getParam(ParamName.redirect));
|
||||
return res.redirect(redirectUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:RestAPI 登录接口
|
||||
@@ -112,7 +127,24 @@ public class SaSsoHandle {
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:单点注销
|
||||
* SSO-Server端:单点注销 [模式一]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public static Object ssoServerLogoutType1() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
||||
|
||||
// 开始处理
|
||||
stpLogic.logout();
|
||||
|
||||
// 返回
|
||||
return ssoLogoutBack(req, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:单点注销 [模式三]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public static Object ssoServerLogout() {
|
||||
@@ -126,7 +158,6 @@ public class SaSsoHandle {
|
||||
String secretkey = req.getParam(ParamName.secretkey);
|
||||
|
||||
// 遍历通知Client端注销会话
|
||||
// SaSsoUtil.singleLogout(secretkey, loginId, url -> cfg.sendHttp.apply(url));
|
||||
// step.1 校验秘钥
|
||||
SaSsoUtil.checkSecretkey(secretkey);
|
||||
|
||||
@@ -134,7 +165,7 @@ public class SaSsoHandle {
|
||||
SaSsoUtil.forEachSloUrl(loginId, url -> cfg.sendHttp.apply(url));
|
||||
|
||||
// step.3 Server端注销
|
||||
stpLogic.logoutByTokenValue(stpLogic.getTokenValueByLoginId(loginId));
|
||||
stpLogic.logout(loginId);
|
||||
|
||||
// 完成
|
||||
return SaSsoConsts.OK;
|
||||
@@ -142,7 +173,7 @@ public class SaSsoHandle {
|
||||
|
||||
|
||||
/**
|
||||
* 处理所有Client端请求
|
||||
* 处理Client端所有请求
|
||||
* @return 处理结果
|
||||
*/
|
||||
public static Object clientRequest() {
|
||||
@@ -206,20 +237,8 @@ public class SaSsoHandle {
|
||||
return res.redirect(serverAuthUrl);
|
||||
} else {
|
||||
// ------- 1、校验ticket,获取账号id
|
||||
Object loginId = null;
|
||||
if(cfg.isHttp) {
|
||||
// 方式1:使用http请求校验ticket
|
||||
String ssoLogoutCall = null;
|
||||
if(cfg.isSlo) {
|
||||
ssoLogoutCall = SaHolder.getRequest().getUrl().replace(Api.ssoLogin, Api.ssoLogoutCall);
|
||||
}
|
||||
String checkUrl = SaSsoUtil.buildCheckTicketUrl(ticket, ssoLogoutCall);
|
||||
Object body = cfg.sendHttp.apply(checkUrl);
|
||||
loginId = (SaFoxUtil.isEmpty(body) ? null : body);
|
||||
} else {
|
||||
// 方式2:直连Redis校验ticket
|
||||
loginId = SaSsoUtil.checkTicket(ticket);
|
||||
}
|
||||
Object loginId = checkTicket(ticket, Api.ssoLogin);
|
||||
|
||||
// Be: 如果开发者自定义了处理逻辑
|
||||
if(cfg.ticketResultHandle != null) {
|
||||
return cfg.ticketResultHandle.apply(loginId, back);
|
||||
@@ -307,7 +326,7 @@ public class SaSsoHandle {
|
||||
/*
|
||||
* 三种情况:
|
||||
* 1. 有back参数,值为SELF -> 回退一级并刷新
|
||||
* 2. 有back参数,值为url -> 跳转back地址
|
||||
* 2. 有back参数,值为url -> 跳转到此url地址
|
||||
* 3. 无back参数 -> 返回json数据
|
||||
*/
|
||||
String back = req.getParam(ParamName.back);
|
||||
@@ -321,4 +340,28 @@ public class SaSsoHandle {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装:校验ticket,取出loginId
|
||||
* @param ticket ticket码
|
||||
* @param currUri 当前路由的uri,用于计算单点注销回调地址
|
||||
* @return loginId
|
||||
*/
|
||||
public static Object checkTicket(String ticket, String currUri) {
|
||||
SaSsoConfig cfg = SaManager.getConfig().getSso();
|
||||
// --------- 两种模式
|
||||
if(cfg.isHttp) {
|
||||
// 模式三:使用http请求校验ticket
|
||||
String ssoLogoutCall = null;
|
||||
if(cfg.isSlo) {
|
||||
ssoLogoutCall = SaHolder.getRequest().getUrl().replace(currUri, Api.ssoLogoutCall);
|
||||
}
|
||||
String checkUrl = SaSsoUtil.buildCheckTicketUrl(ticket, ssoLogoutCall);
|
||||
Object body = cfg.sendHttp.apply(checkUrl);
|
||||
return (SaFoxUtil.isEmpty(body) ? null : body);
|
||||
} else {
|
||||
// 模式二:直连Redis校验ticket
|
||||
return SaSsoUtil.checkTicket(ticket);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -328,10 +328,13 @@ public class SaSsoTemplate {
|
||||
* @param fun 调用方法
|
||||
*/
|
||||
public void forEachSloUrl(Object loginId, CallSloUrlFunction fun) {
|
||||
String secretkey = SaManager.getConfig().getSso().getSecretkey();
|
||||
Set<String> urlSet = stpLogic.getSessionByLoginId(loginId).get(SaSsoConsts.SLO_CALLBACK_SET_KEY,
|
||||
() -> new HashSet<String>());
|
||||
SaSession session = stpLogic.getSessionByLoginId(loginId, false);
|
||||
if(session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String secretkey = SaManager.getConfig().getSso().getSecretkey();
|
||||
Set<String> urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, () -> new HashSet<String>());
|
||||
for (String url : urlSet) {
|
||||
// 拼接:login参数、秘钥参数
|
||||
url = SaFoxUtil.joinParam(url, ParamName.loginId, loginId);
|
||||
|
@@ -443,7 +443,7 @@ public class StpLogic {
|
||||
* @param appendFun 追加操作
|
||||
* @param isLogoutSession 是否注销 User-Session
|
||||
*/
|
||||
private void clearTokenCommonMethod(Object loginId, String device, Consumer<String> appendFun, boolean isLogoutSession) {
|
||||
protected void clearTokenCommonMethod(Object loginId, String device, Consumer<String> appendFun, boolean isLogoutSession) {
|
||||
// 1. 如果此账号尚未登录,则不执行任何操作
|
||||
SaSession session = getSessionByLoginId(loginId, false);
|
||||
if(session == null) {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package cn.dev33.satoken.strategy;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
@@ -24,6 +25,7 @@ import cn.dev33.satoken.session.SaSession;
|
||||
* </p>
|
||||
*
|
||||
* @author kong
|
||||
* @param <T>
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@@ -80,12 +82,22 @@ public final class SaStrategy {
|
||||
|
||||
/**
|
||||
* 对一个 [元素] 对象进行注解校验 (注解鉴权内部实现)
|
||||
* <p> 参数 [target元素]
|
||||
* <p> 参数 [element元素]
|
||||
*/
|
||||
public Consumer<AnnotatedElement> checkElementAnnotation = (element) -> {
|
||||
// 为了兼容旧版本
|
||||
SaManager.getSaTokenAction().validateAnnotation(element);
|
||||
};
|
||||
|
||||
/**
|
||||
* 从元素上获取注解(注解鉴权内部实现)
|
||||
* <p> 参数 [element元素,要获取的注解类型]
|
||||
*/
|
||||
public BiFunction<AnnotatedElement, Class<? extends Annotation> , Annotation> getAnnotation = (element, annotationClass)->{
|
||||
// 默认使用jdk的注解处理器
|
||||
return element.getAnnotation(annotationClass);
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// 重写策略 set连缀风格
|
||||
@@ -137,7 +149,7 @@ public final class SaStrategy {
|
||||
|
||||
/**
|
||||
* 对一个 [元素] 对象进行注解校验 (注解鉴权内部实现)
|
||||
* <p> 参数 [target元素]
|
||||
* <p> 参数 [element元素]
|
||||
* @param checkElementAnnotation /
|
||||
* @return 对象自身
|
||||
*/
|
||||
@@ -146,5 +158,16 @@ public final class SaStrategy {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从元素上获取注解(注解鉴权内部实现)
|
||||
* <p> 参数 [element元素,要获取的注解类型]
|
||||
* @param getAnnotation /
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaStrategy setGetAnnotation(BiFunction<AnnotatedElement, Class<? extends Annotation> , Annotation> getAnnotation) {
|
||||
this.getAnnotation = getAnnotation;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ public class SaTokenConsts {
|
||||
/**
|
||||
* Sa-Token 当前版本号
|
||||
*/
|
||||
public static final String VERSION_NO = "v1.26.2";
|
||||
public static final String VERSION_NO = "v1.27.0";
|
||||
|
||||
/**
|
||||
* Sa-Token 开源地址
|
||||
|
@@ -10,13 +10,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.annotation.SaCheckBasic;
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.annotation.SaCheckSafe;
|
||||
import cn.dev33.satoken.basic.SaBasicUtil;
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.filter.SaServletFilter;
|
||||
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
|
||||
@@ -86,27 +80,8 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
@PostConstruct
|
||||
public void rewriteSaStrategy() {
|
||||
// 重写Sa-Token的注解处理器,增加注解合并功能
|
||||
SaStrategy.me.setCheckElementAnnotation(element -> {
|
||||
if(AnnotatedElementUtils.isAnnotated(element, SaCheckLogin.class)) {
|
||||
SaCheckLogin at = AnnotatedElementUtils.getMergedAnnotation(element, SaCheckLogin.class);
|
||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||
}
|
||||
if(AnnotatedElementUtils.isAnnotated(element, SaCheckRole.class)) {
|
||||
SaCheckRole at = AnnotatedElementUtils.getMergedAnnotation(element, SaCheckRole.class);
|
||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||
}
|
||||
if(AnnotatedElementUtils.isAnnotated(element, SaCheckPermission.class)) {
|
||||
SaCheckPermission at = AnnotatedElementUtils.getMergedAnnotation(element, SaCheckPermission.class);
|
||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||
}
|
||||
if(AnnotatedElementUtils.isAnnotated(element, SaCheckSafe.class)) {
|
||||
SaCheckSafe at = AnnotatedElementUtils.getMergedAnnotation(element, SaCheckSafe.class);
|
||||
SaManager.getStpLogic(null).checkByAnnotation(at);
|
||||
}
|
||||
if(AnnotatedElementUtils.isAnnotated(element, SaCheckBasic.class)) {
|
||||
SaCheckBasic at = AnnotatedElementUtils.getMergedAnnotation(element, SaCheckBasic.class);
|
||||
SaBasicUtil.check(at.realm(), at.account());
|
||||
}
|
||||
SaStrategy.me.setGetAnnotation((element, annotationClass) -> {
|
||||
return AnnotatedElementUtils.getMergedAnnotation(element, SaCheckLogin.class);
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,8 @@ package com.pj.satoken.at;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.fun.SaFunction;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
@@ -14,6 +16,7 @@ import cn.dev33.satoken.stp.StpUtil;
|
||||
* Sa-Token 权限认证工具类
|
||||
* @author kong
|
||||
*/
|
||||
@Component
|
||||
public class StpUserUtil {
|
||||
|
||||
/**
|
||||
|
@@ -42,9 +42,9 @@ sa.ajax = function(url, data, successFn) {
|
||||
// ----------------------------------- 相关事件 -----------------------------------
|
||||
|
||||
// 检查当前是否已经登录,如果已登录则直接开始跳转,如果未登录则等待用户输入账号密码
|
||||
sa.ajax("/getRedirectUrl", {redirect: getParam('redirect', '')}, function(res) {
|
||||
sa.ajax("/getRedirectUrl", {redirect: getParam('redirect', ''), mode: getParam('mode', '')}, function(res) {
|
||||
if(res.code == 200) {
|
||||
// redirect地址有效,开始跳转
|
||||
// 已登录,并且redirect地址有效,开始跳转
|
||||
location.href = decodeURIComponent(res.data);
|
||||
} else if(res.code == 401) {
|
||||
console.log('未登录');
|
@@ -3,7 +3,7 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-demo-sso3-server</artifactId>
|
||||
<artifactId>sa-token-demo-sso-server</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot依赖 -->
|
||||
<!-- SpringBoot Web依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
@@ -40,20 +40,18 @@
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 提供redis连接池 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 视图引擎 -->
|
||||
<!-- 视图引擎(在前后端不分离模式下提供视图支持) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Http请求工具 -->
|
||||
<!-- Http请求工具(在模式三的单点注销功能下用到,如不需要可以注释掉) -->
|
||||
<dependency>
|
||||
<groupId>com.ejlchina</groupId>
|
||||
<artifactId>okhttps</artifactId>
|
@@ -8,7 +8,7 @@ public class SaSsoServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSsoServerApplication.class, args);
|
||||
System.out.println("\nSa-Token-SSO 认证中心启动成功");
|
||||
System.out.println("\n------ Sa-Token-SSO 统一认证中心启动成功 ");
|
||||
}
|
||||
|
||||
}
|
@@ -4,8 +4,10 @@ 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.SaSsoConsts;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
@@ -21,15 +23,22 @@ public class H5Controller {
|
||||
* 获取 redirectUrl
|
||||
*/
|
||||
@RequestMapping("/getRedirectUrl")
|
||||
private Object getRedirectUrl(String redirect) {
|
||||
private Object getRedirectUrl(String redirect, String mode) {
|
||||
// 未登录情况下,返回 code=401
|
||||
if(StpUtil.isLogin() == false) {
|
||||
return SaResult.code(401);
|
||||
}
|
||||
// 已登录情况下,构建 redirectUrl
|
||||
if(SaSsoConsts.MODE_SIMPLE.equals(mode)) {
|
||||
// 模式一
|
||||
SaSsoUtil.checkRedirectUrl(SaFoxUtil.decoderUrl(redirect));
|
||||
return SaResult.data(redirect);
|
||||
} else {
|
||||
// 模式二或模式三
|
||||
String redirectUrl = SaSsoUtil.buildRedirectUrl(StpUtil.getLoginId(), redirect);
|
||||
return SaResult.data(redirectUrl);
|
||||
}
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
@@ -0,0 +1,23 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
@@ -1,11 +1,12 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import com.ejlchina.okhttps.OkHttps;
|
||||
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.sso.SaSsoHandle;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
@@ -34,28 +35,26 @@ public class SsoServerController {
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaTokenConfig cfg) {
|
||||
cfg.sso
|
||||
|
||||
// 配置:未登录时返回的View
|
||||
.setNotLoginView(() -> {
|
||||
cfg.sso.setNotLoginView(() -> {
|
||||
return new ModelAndView("sa-login.html");
|
||||
})
|
||||
});
|
||||
|
||||
// 配置:登录处理函数
|
||||
.setDoLoginHandle((name, pwd) -> {
|
||||
cfg.sso.setDoLoginHandle((name, pwd) -> {
|
||||
// 此处仅做模拟登录,真实环境应该查询数据进行登录
|
||||
if("sa".equals(name) && "123456".equals(pwd)) {
|
||||
StpUtil.login(10001);
|
||||
return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue());
|
||||
}
|
||||
return SaResult.error("登录失败!");
|
||||
})
|
||||
;
|
||||
}
|
||||
});
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
// 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉)
|
||||
cfg.sso.setSendHttp(url -> {
|
||||
return OkHttps.sync(url).get().getBody().toString();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@@ -4,25 +4,32 @@ server:
|
||||
|
||||
# Sa-Token 配置
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
# -------------- SSO-模式一相关配置 (非模式一不需要配置)
|
||||
# cookie:
|
||||
# 配置Cookie作用域
|
||||
# domain: stp.com
|
||||
|
||||
# ------- SSO-模式二相关配置
|
||||
sso:
|
||||
# Ticket有效期 (单位: 秒),默认五分钟
|
||||
ticket-timeout: 300
|
||||
# 所有允许的授权回调地址
|
||||
# allow-url: http://sa-sso-client1.com:9001/sso/login, http://sa-sso-client2.com:9001/sso/login, http://sa-sso-client3.com:9001/sso/login
|
||||
allow-url: "*"
|
||||
# 接口调用秘钥(用于SSO模式三的单点注销功能)
|
||||
secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
# 使用Http请求校验ticket
|
||||
is-http: true
|
||||
# 打开单点注销功能
|
||||
# 是否打开单点注销功能
|
||||
is-slo: true
|
||||
|
||||
# ------- SSO-模式三相关配置 (下面的配置在SSO模式三并且 is-slo=true 时打开) -------
|
||||
# 是否打开模式三
|
||||
isHttp: true
|
||||
# 接口调用秘钥(用于SSO模式三的单点注销功能)
|
||||
secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
# ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器(文档有步骤说明)
|
||||
|
||||
spring:
|
||||
# Redis配置
|
||||
# Redis配置 (SSO模式一和模式二使用Redis来同步会话)
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 5
|
||||
database: 1
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 701 B After Width: | Height: | Size: 701 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
@@ -13,7 +13,7 @@ public class SaSsoClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSsoClientApplication.class, args);
|
||||
System.out.println("\nSa-Token-SSO Client端启动成功");
|
||||
System.out.println("\nSa-Token SSO模式一 Client端启动成功");
|
||||
}
|
||||
|
||||
}
|
@@ -15,16 +15,14 @@ import cn.dev33.satoken.util.SaResult;
|
||||
@RestController
|
||||
public class SsoClientController {
|
||||
|
||||
/*
|
||||
* SSO-Client端:首页
|
||||
*/
|
||||
// SSO-Client端:首页
|
||||
@RequestMapping("/")
|
||||
public String index() {
|
||||
String authUrl = SaManager.getConfig().getSso().getAuthUrl();
|
||||
String solUrl = SaManager.getConfig().getSso().getSloUrl();
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
|
||||
"<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" +
|
||||
"<p><a href=\"javascript:location.href='" + authUrl + "?redirect=' + encodeURIComponent(location.href);\">登录</a> " +
|
||||
"<p><a href=\"javascript:location.href='" + authUrl + "?mode=simple&redirect=' + encodeURIComponent(location.href);\">登录</a> " +
|
||||
"<a href=\"javascript:location.href='" + solUrl + "?back=' + encodeURIComponent(location.href);\">注销</a> </p>";
|
||||
return str;
|
||||
}
|
||||
|
@@ -6,9 +6,9 @@ server:
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso:
|
||||
# SSO-Server端 单点登录地址
|
||||
# SSO-Server端-单点登录授权地址
|
||||
auth-url: http://sso.stp.com:9000/sso/auth
|
||||
# SSO-Server端 单点注销地址
|
||||
# SSO-Server端-单点注销地址
|
||||
slo-url: http://sso.stp.com:9000/sso/logout
|
||||
|
||||
# 配置Sa-Token单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis)
|
||||
|
@@ -1,57 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-demo-sso1-server</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.26.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token整合redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 视图引擎 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
@@ -1,19 +0,0 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* SSO模式一,Server端 Demo
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaSsoServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSsoServerApplication.class, args);
|
||||
System.out.println("\nSa-Token-SSO 认证中心启动成功");
|
||||
}
|
||||
|
||||
}
|
@@ -1,62 +0,0 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO Server端 Controller
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
public class SsoServerController {
|
||||
|
||||
/*
|
||||
* SSO-Server端:统一登录地址
|
||||
*/
|
||||
@RequestMapping("/sso/auth")
|
||||
public Object ssoAuth(String redirect) {
|
||||
// 如果尚未登录,则返回登录视图进行登录
|
||||
if(StpUtil.isLogin() == false) {
|
||||
return new ModelAndView("sa-login.html");
|
||||
}
|
||||
// 如果已登录,则原路返回到 Client端
|
||||
return SaHolder.getResponse().redirect(redirect);
|
||||
}
|
||||
|
||||
// 处理登录请求
|
||||
@RequestMapping("/sso/doLogin")
|
||||
public SaResult ssoDoLogin(String name, String pwd) {
|
||||
// 模拟登录
|
||||
if("sa".equals(name) && "123456".equals(pwd)) {
|
||||
StpUtil.login(10001);
|
||||
return SaResult.ok("登录成功!");
|
||||
}
|
||||
return SaResult.error("登录失败!");
|
||||
}
|
||||
|
||||
// 单点注销地址
|
||||
@RequestMapping("/sso/logout")
|
||||
public Object ssoLogout(String back) {
|
||||
StpUtil.logout();
|
||||
if(SaFoxUtil.isEmpty(back)) {
|
||||
return SaResult.ok();
|
||||
}
|
||||
return SaHolder.getResponse().redirect(back);
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 9000
|
||||
|
||||
# Sa-Token配置
|
||||
sa-token:
|
||||
# 写入Cookie时显式指定的作用域, 用于单点登录二级域名共享Cookie
|
||||
cookie-domain: stp.com
|
||||
|
||||
spring:
|
||||
# Redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 1
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -1,45 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<title>Sa-SSO-Server 认证中心-登录</title>
|
||||
<meta charset="utf-8">
|
||||
<base th:href="@{/}" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<link rel="stylesheet" href="./sa-res/login.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="view-box">
|
||||
<div class="bg-1"></div>
|
||||
<div class="bg-2"></div>
|
||||
<div class="content-box">
|
||||
<div class="login-box">
|
||||
<div class="from-box">
|
||||
<h2 class="from-title">Sa-SSO-Server 认证中心</h2>
|
||||
<div class="from-item">
|
||||
<input class="s-input" name="name" placeholder="请输入账号" />
|
||||
</div>
|
||||
<div class="from-item">
|
||||
<input class="s-input" name="pwd" type="password" placeholder="请输入密码" />
|
||||
</div>
|
||||
<div class="from-item">
|
||||
<button class="s-input s-btn login-btn">登录</button>
|
||||
</div>
|
||||
<div class="from-item reset-box">
|
||||
<a href="javascript: location.reload();" >刷新</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部 版权 -->
|
||||
<div style="position: absolute; bottom: 40px; width: 100%; text-align: center; color: #666;">
|
||||
This page is provided by Sa-Token-SSO
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- scripts -->
|
||||
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script src="https://www.layuicdn.com/layer-v3.1.1/layer.js"></script>
|
||||
<script src="./sa-res/login.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
12
sa-token-demo/sa-token-demo-sso1/.gitignore
vendored
@@ -1,12 +0,0 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.idea/
|
||||
|
||||
.factorypath
|
@@ -1,40 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-demo-sso1</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.26.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- springboot依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
@@ -1,36 +0,0 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SaSsoApplication {
|
||||
|
||||
/*
|
||||
此 demo 意在使用最少的代码演示一下SSO模式一的认证原理
|
||||
1、改hosts(C:\windows\system32\drivers\etc\hosts)
|
||||
127.0.0.1 sso.stp.com
|
||||
127.0.0.1 s1.stp.com
|
||||
127.0.0.1 s2.stp.com
|
||||
127.0.0.1 s3.stp.com
|
||||
2、运行项目
|
||||
启动 SaSsoApplication
|
||||
3、浏览器访问
|
||||
http://s1.stp.com:8081/sso/isLogin
|
||||
http://s2.stp.com:8081/sso/isLogin
|
||||
http://s3.stp.com:8081/sso/isLogin
|
||||
均显示未登录
|
||||
4、然后访问任意节点的登录接口:
|
||||
http://s1.stp.com:8081/sso/doLogin
|
||||
5、重复步骤3,刷新三个地址
|
||||
均显示已登录
|
||||
*/
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSsoApplication.class, args);
|
||||
System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
|
||||
}
|
||||
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* 测试: 同域单点登录
|
||||
* @author kong
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/sso/")
|
||||
public class SsoController {
|
||||
|
||||
// 测试:进行登录
|
||||
@RequestMapping("doLogin")
|
||||
public SaResult doLogin(@RequestParam(defaultValue = "10001") String id) {
|
||||
System.out.println("---------------- 进行登录 ");
|
||||
StpUtil.login(id);
|
||||
return SaResult.ok("登录成功: " + id);
|
||||
}
|
||||
|
||||
// 测试:是否登录
|
||||
@RequestMapping("isLogin")
|
||||
public SaResult isLogin() {
|
||||
System.out.println("---------------- 是否登录 ");
|
||||
boolean isLogin = StpUtil.isLogin();
|
||||
return SaResult.ok("是否登录: " + isLogin);
|
||||
}
|
||||
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 8081
|
||||
|
||||
sa-token:
|
||||
# 写入Cookie时显式指定的作用域, 用于单点登录二级域名共享Cookie
|
||||
cookie-domain: stp.com
|
||||
|
@@ -8,7 +8,7 @@ public class SaSsoClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSsoClientApplication.class, args);
|
||||
System.out.println("\nSa-Token-SSO Client端启动成功");
|
||||
System.out.println("\nSa-Token SSO模式二 Client端启动成功");
|
||||
}
|
||||
|
||||
}
|
@@ -4,6 +4,7 @@ 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.SaSsoHandle;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
@@ -33,7 +34,7 @@ public class H5Controller {
|
||||
// 根据ticket进行登录
|
||||
@RequestMapping("/doLoginByTicket")
|
||||
public SaResult doLoginByTicket(String ticket) {
|
||||
Object loginId = checkTicket(ticket);
|
||||
Object loginId = SaSsoHandle.checkTicket(ticket, "/doLoginByTicket");
|
||||
if(loginId != null) {
|
||||
StpUtil.login(loginId);
|
||||
return SaResult.data(StpUtil.getTokenValue());
|
||||
@@ -41,11 +42,6 @@ public class H5Controller {
|
||||
return SaResult.error("无效ticket:" + ticket);
|
||||
}
|
||||
|
||||
// 校验 Ticket码,获取账号Id
|
||||
private Object checkTicket(String ticket) {
|
||||
return SaSsoUtil.checkTicket(ticket);
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
|
@@ -8,7 +8,7 @@ sa-token:
|
||||
sso:
|
||||
# SSO-Server端 统一认证地址
|
||||
auth-url: http://sa-sso-server.com:9000/sso/auth
|
||||
# auth-url: http://127.0.0.1:8848/sa-token-demo-sso2-server-h5/sso-auth.html
|
||||
# auth-url: http://127.0.0.1:8848/sa-token-demo-sso-server-h5/sso-auth.html
|
||||
# 是否打开单点注销接口
|
||||
is-slo: true
|
||||
|
||||
|
@@ -1,12 +0,0 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.idea/
|
||||
|
||||
.factorypath
|
@@ -1,59 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-demo-sso2-server</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.26.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token整合redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 提供redis连接池 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 视图引擎 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
@@ -1,43 +0,0 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 9000
|
||||
|
||||
# Sa-Token配置
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso:
|
||||
# Ticket有效期 (单位: 秒),默认五分钟
|
||||
ticket-timeout: 300
|
||||
# 所有允许的授权回调地址
|
||||
# allow-url: http://sa-sso-client1.com:9001/sso/login, http://sa-sso-client2.com:9001/sso/login, http://sa-sso-client3.com:9001/sso/login
|
||||
allow-url: "*"
|
||||
|
||||
spring:
|
||||
# Redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 1
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -1,59 +0,0 @@
|
||||
*{margin: 0; padding: 0;}
|
||||
body{font-family: Helvetica Neue,Helvetica,PingFang SC,Tahoma,Arial,sans-serif;}
|
||||
::-webkit-input-placeholder{color: #ccc;}
|
||||
|
||||
/* 视图盒子 */
|
||||
.view-box{position: relative; width: 100vw; height: 100vh; overflow: hidden;}
|
||||
/* 背景 EAEFF3 */
|
||||
.bg-1{height: 50%; background: linear-gradient(to bottom right, #0466c5, #3496F5);}
|
||||
.bg-2{height: 50%; background-color: #EAEFF3;}
|
||||
|
||||
/* 渐变背景 */
|
||||
/*.bg-1{
|
||||
background-size: 500%;
|
||||
background-image: linear-gradient(125deg,#0466c5,#3496F5,#0466c5,#3496F5,#0466c5,#2496F5);
|
||||
animation: bganimation 30s infinite;
|
||||
}
|
||||
@keyframes bganimation{
|
||||
0%{background-position: 0% 50%;}
|
||||
50%{background-position: 100% 50%;}
|
||||
100%{background-position: 0% 50%;}
|
||||
} */
|
||||
/* 背景 */
|
||||
.bg-1{background: #101C34;}
|
||||
.bg-2{background: #101C34;}
|
||||
/* .bg-1{height: 100%; background-image: url(./login-bg.png); background-size: 100% 100%;} */
|
||||
|
||||
|
||||
/* 内容盒子 */
|
||||
.content-box{position: absolute; width: 100vw; height: 100vh; top: 0px;}
|
||||
|
||||
/* 登录盒子 */
|
||||
/* .login-box{width: 400px; height: 400px; position: absolute; left: calc(50% - 200px); top: calc(50% - 200px); max-width: 90%; } */
|
||||
.login-box{width: 400px; margin: auto; max-width: 90%; height: 100%;}
|
||||
.login-box{display: flex; align-items: center; text-align: center;}
|
||||
|
||||
/* 表单 */
|
||||
.from-box{flex: 1; padding: 20px 50px; background-color: #FFF;}
|
||||
.from-box{border-radius: 1px; box-shadow: 1px 1px 20px #666;}
|
||||
.from-title{margin-top: 20px; margin-bottom: 30px; text-align: center;}
|
||||
|
||||
/* 输入框 */
|
||||
.from-item{border: 0px #000 solid; margin-bottom: 15px;}
|
||||
.s-input{width: 100%; line-height: 32px; height: 32px; text-indent: 1em; outline: 0; border: 1px #ccc solid; border-radius: 3px; transition: all 0.2s;}
|
||||
.s-input{font-size: 12px;}
|
||||
.s-input:focus{border-color: #409eff}
|
||||
|
||||
/* 登录按钮 */
|
||||
.s-btn{ text-indent: 0; cursor: pointer; background-color: #409EFF; border-color: #409EFF; color: #FFF;}
|
||||
.s-btn:hover{background-color: #50aEFF;}
|
||||
|
||||
/* 重置按钮 */
|
||||
.reset-box{text-align: left; font-size: 12px;}
|
||||
.reset-box a{text-decoration: none;}
|
||||
.reset-box a:hover{text-decoration: underline;}
|
||||
|
||||
/* loading框样式 */
|
||||
.ajax-layer-load.layui-layer-dialog{min-width: 0px !important; background-color: rgba(0,0,0,0.85);}
|
||||
.ajax-layer-load.layui-layer-dialog .layui-layer-content{padding: 10px 20px 10px 40px; color: #FFF;}
|
||||
.ajax-layer-load.layui-layer-dialog .layui-layer-content .layui-layer-ico{width: 20px; height: 20px; background-size: 20px 20px; top: 12px; }
|
@@ -1,65 +0,0 @@
|
||||
// sa
|
||||
var sa = {};
|
||||
|
||||
// 打开loading
|
||||
sa.loading = function(msg) {
|
||||
layer.closeAll(); // 开始前先把所有弹窗关了
|
||||
return layer.msg(msg, {icon: 16, shade: 0.3, time: 1000 * 20, skin: 'ajax-layer-load' });
|
||||
};
|
||||
|
||||
// 隐藏loading
|
||||
sa.hideLoading = function() {
|
||||
layer.closeAll();
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------- 登录事件 -----------------------------------
|
||||
|
||||
$('.login-btn').click(function(){
|
||||
sa.loading("正在登录...");
|
||||
// 开始登录
|
||||
setTimeout(function() {
|
||||
$.ajax({
|
||||
url: "sso/doLogin",
|
||||
type: "post",
|
||||
data: {
|
||||
name: $('[name=name]').val(),
|
||||
pwd: $('[name=pwd]').val()
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(res){
|
||||
console.log('返回数据:', res);
|
||||
sa.hideLoading();
|
||||
if(res.code == 200) {
|
||||
layer.msg('登录成功', {anim: 0, icon: 6 });
|
||||
setTimeout(function() {
|
||||
location.reload();
|
||||
}, 800)
|
||||
} else {
|
||||
layer.msg(res.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));
|
||||
}
|
||||
});
|
||||
}, 400);
|
||||
});
|
||||
|
||||
// 绑定回车事件
|
||||
$('[name=name],[name=pwd]').bind('keypress', function(event){
|
||||
if(event.keyCode == "13") {
|
||||
$('.login-btn').click();
|
||||
}
|
||||
});
|
||||
|
||||
// 输入框获取焦点
|
||||
$("[name=name]").focus();
|
||||
|
||||
// 打印信息
|
||||
var str = "This page is provided by Sa-Token, Please refer to: " + "http://sa-token.dev33.cn/";
|
||||
console.log(str);
|
@@ -8,7 +8,7 @@ public class SaSsoClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSsoClientApplication.class, args);
|
||||
System.out.println("\nSa-Token-SSO Client端启动成功");
|
||||
System.out.println("\nSa-Token SSO模式三 Client端启动成功");
|
||||
}
|
||||
|
||||
}
|
@@ -44,12 +44,10 @@ public class SsoClientController {
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaTokenConfig cfg) {
|
||||
cfg.sso
|
||||
// 配置Http请求处理器
|
||||
.setSendHttp(url -> {
|
||||
cfg.sso.setSendHttp(url -> {
|
||||
return OkHttps.sync(url).get().getBody().toString();
|
||||
})
|
||||
;
|
||||
});
|
||||
}
|
||||
|
||||
// 查询我的账号信息
|
||||
|
@@ -25,7 +25,7 @@ spring:
|
||||
# 配置Sa-Token单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis)
|
||||
redis:
|
||||
# Redis数据库索引
|
||||
database: 6
|
||||
database: 2
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
|
@@ -1,12 +0,0 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.idea/
|
||||
|
||||
.factorypath
|
@@ -1,14 +0,0 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SaSsoServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSsoServerApplication.class, args);
|
||||
System.out.println("\nSa-Token-SSO 认证中心启动成功");
|
||||
}
|
||||
|
||||
}
|
@@ -1,85 +0,0 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import com.ejlchina.okhttps.OkHttps;
|
||||
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.sso.SaSsoHandle;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO Server端 Controller
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
public class SsoServerController {
|
||||
|
||||
/*
|
||||
* SSO-Server端:处理所有SSO相关请求
|
||||
* http://{host}:{port}/sso/auth -- 单点登录授权地址,接受参数:redirect=授权重定向地址
|
||||
* http://{host}:{port}/sso/doLogin -- 账号密码登录接口,接受参数:name、pwd
|
||||
* http://{host}:{port}/sso/checkTicket -- Ticket校验接口(isHttp=true时打开),接受参数:ticket=ticket码、ssoLogoutCall=单点注销回调地址 [可选]
|
||||
* http://{host}:{port}/sso/logout -- 单点注销地址(isSlo=true时打开),接受参数:loginId=账号id、secretkey=接口调用秘钥
|
||||
*/
|
||||
@RequestMapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
return SaSsoHandle.serverRequest();
|
||||
}
|
||||
|
||||
// 自定义接口:获取userinfo
|
||||
@RequestMapping("/sso/userinfo")
|
||||
public Object userinfo(String loginId, String secretkey) {
|
||||
System.out.println("---------------- 获取userinfo --------");
|
||||
|
||||
// 校验调用秘钥
|
||||
SaSsoUtil.checkSecretkey(secretkey);
|
||||
|
||||
// 自定义返回结果(模拟)
|
||||
return SaResult.ok()
|
||||
.set("id", loginId)
|
||||
.set("name", "linxiaoyu")
|
||||
.set("sex", "女")
|
||||
.set("age", 18);
|
||||
}
|
||||
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaTokenConfig cfg) {
|
||||
cfg.sso
|
||||
// SSO-Server端:未登录时返回的View
|
||||
.setNotLoginView(() -> {
|
||||
return new ModelAndView("sa-login.html");
|
||||
})
|
||||
// SSO-Server端:登录函数
|
||||
.setDoLoginHandle((name, pwd) -> {
|
||||
// 此处仅做模拟登录,真实环境应该查询数据进行登录
|
||||
if("sa".equals(name) && "123456".equals(pwd)) {
|
||||
StpUtil.login(10001);
|
||||
return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue());
|
||||
}
|
||||
return SaResult.error("登录失败!");
|
||||
})
|
||||
// 配置Http请求处理器
|
||||
.setSendHttp(url -> {
|
||||
// 此处为了提高响应速度这里可将sync换为async
|
||||
return OkHttps.sync(url).get();
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
@@ -1,2 +0,0 @@
|
||||
/*! layer mobile-v2.0.0 Web弹层组件 MIT License http://layer.layui.com/mobile By 贤心 */
|
||||
;!function(e){"use strict";var t=document,n="querySelectorAll",i="getElementsByClassName",a=function(e){return t[n](e)},s={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},l={extend:function(e){var t=JSON.parse(JSON.stringify(s));for(var n in e)t[n]=e[n];return t},timer:{},end:{}};l.touch=function(e,t){e.addEventListener("click",function(e){t.call(this,e)},!1)};var r=0,o=["layui-m-layer"],c=function(e){var t=this;t.config=l.extend(e),t.view()};c.prototype.view=function(){var e=this,n=e.config,s=t.createElement("div");e.id=s.id=o[0]+r,s.setAttribute("class",o[0]+" "+o[0]+(n.type||0)),s.setAttribute("index",r);var l=function(){var e="object"==typeof n.title;return n.title?'<h3 style="'+(e?n.title[1]:"")+'">'+(e?n.title[0]:n.title)+"</h3>":""}(),c=function(){"string"==typeof n.btn&&(n.btn=[n.btn]);var e,t=(n.btn||[]).length;return 0!==t&&n.btn?(e='<span yes type="1">'+n.btn[0]+"</span>",2===t&&(e='<span no type="0">'+n.btn[1]+"</span>"+e),'<div class="layui-m-layerbtn">'+e+"</div>"):""}();if(n.fixed||(n.top=n.hasOwnProperty("top")?n.top:100,n.style=n.style||"",n.style+=" top:"+(t.body.scrollTop+n.top)+"px"),2===n.type&&(n.content='<i></i><i class="layui-m-layerload"></i><i></i><p>'+(n.content||"")+"</p>"),n.skin&&(n.anim="up"),"msg"===n.skin&&(n.shade=!1),s.innerHTML=(n.shade?"<div "+("string"==typeof n.shade?'style="'+n.shade+'"':"")+' class="layui-m-layershade"></div>':"")+'<div class="layui-m-layermain" '+(n.fixed?"":'style="position:static;"')+'><div class="layui-m-layersection"><div class="layui-m-layerchild '+(n.skin?"layui-m-layer-"+n.skin+" ":"")+(n.className?n.className:"")+" "+(n.anim?"layui-m-anim-"+n.anim:"")+'" '+(n.style?'style="'+n.style+'"':"")+">"+l+'<div class="layui-m-layercont">'+n.content+"</div>"+c+"</div></div></div>",!n.type||2===n.type){var d=t[i](o[0]+n.type),y=d.length;y>=1&&layer.close(d[0].getAttribute("index"))}document.body.appendChild(s);var u=e.elem=a("#"+e.id)[0];n.success&&n.success(u),e.index=r++,e.action(n,u)},c.prototype.action=function(e,t){var n=this;e.time&&(l.timer[n.index]=setTimeout(function(){layer.close(n.index)},1e3*e.time));var a=function(){var t=this.getAttribute("type");0==t?(e.no&&e.no(),layer.close(n.index)):e.yes?e.yes(n.index):layer.close(n.index)};if(e.btn)for(var s=t[i]("layui-m-layerbtn")[0].children,r=s.length,o=0;o<r;o++)l.touch(s[o],a);if(e.shade&&e.shadeClose){var c=t[i]("layui-m-layershade")[0];l.touch(c,function(){layer.close(n.index,e.end)})}e.end&&(l.end[n.index]=e.end)},e.layer={v:"2.0",index:r,open:function(e){var t=new c(e||{});return t.index},close:function(e){var n=a("#"+o[0]+e)[0];n&&(n.innerHTML="",t.body.removeChild(n),clearTimeout(l.timer[e]),delete l.timer[e],"function"==typeof l.end[e]&&l.end[e](),delete l.end[e])},closeAll:function(){for(var e=t[i](o[0]),n=0,a=e.length;n<a;n++)layer.close(0|e[0].getAttribute("index"))}},"function"==typeof define?define(function(){return layer}):function(){var e=document.scripts,n=e[e.length-1],i=n.src,a=i.substring(0,i.lastIndexOf("/")+1);n.getAttribute("merge")||document.head.appendChild(function(){var e=t.createElement("link");return e.href=a+"need/layer.css?2.0",e.type="text/css",e.rel="styleSheet",e.id="layermcss",e}())}()}(window);
|
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 701 B |
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,59 +0,0 @@
|
||||
*{margin: 0; padding: 0;}
|
||||
body{font-family: Helvetica Neue,Helvetica,PingFang SC,Tahoma,Arial,sans-serif;}
|
||||
::-webkit-input-placeholder{color: #ccc;}
|
||||
|
||||
/* 视图盒子 */
|
||||
.view-box{position: relative; width: 100vw; height: 100vh; overflow: hidden;}
|
||||
/* 背景 EAEFF3 */
|
||||
.bg-1{height: 50%; background: linear-gradient(to bottom right, #0466c5, #3496F5);}
|
||||
.bg-2{height: 50%; background-color: #EAEFF3;}
|
||||
|
||||
/* 渐变背景 */
|
||||
/*.bg-1{
|
||||
background-size: 500%;
|
||||
background-image: linear-gradient(125deg,#0466c5,#3496F5,#0466c5,#3496F5,#0466c5,#2496F5);
|
||||
animation: bganimation 30s infinite;
|
||||
}
|
||||
@keyframes bganimation{
|
||||
0%{background-position: 0% 50%;}
|
||||
50%{background-position: 100% 50%;}
|
||||
100%{background-position: 0% 50%;}
|
||||
} */
|
||||
/* 背景 */
|
||||
.bg-1{background: #101C34;}
|
||||
.bg-2{background: #101C34;}
|
||||
/* .bg-1{height: 100%; background-image: url(./login-bg.png); background-size: 100% 100%;} */
|
||||
|
||||
|
||||
/* 内容盒子 */
|
||||
.content-box{position: absolute; width: 100vw; height: 100vh; top: 0px;}
|
||||
|
||||
/* 登录盒子 */
|
||||
/* .login-box{width: 400px; height: 400px; position: absolute; left: calc(50% - 200px); top: calc(50% - 200px); max-width: 90%; } */
|
||||
.login-box{width: 400px; margin: auto; max-width: 90%; height: 100%;}
|
||||
.login-box{display: flex; align-items: center; text-align: center;}
|
||||
|
||||
/* 表单 */
|
||||
.from-box{flex: 1; padding: 20px 50px; background-color: #FFF;}
|
||||
.from-box{border-radius: 1px; box-shadow: 1px 1px 20px #666;}
|
||||
.from-title{margin-top: 20px; margin-bottom: 30px; text-align: center;}
|
||||
|
||||
/* 输入框 */
|
||||
.from-item{border: 0px #000 solid; margin-bottom: 15px;}
|
||||
.s-input{width: 100%; line-height: 32px; height: 32px; text-indent: 1em; outline: 0; border: 1px #ccc solid; border-radius: 3px; transition: all 0.2s;}
|
||||
.s-input{font-size: 12px;}
|
||||
.s-input:focus{border-color: #409eff}
|
||||
|
||||
/* 登录按钮 */
|
||||
.s-btn{ text-indent: 0; cursor: pointer; background-color: #409EFF; border-color: #409EFF; color: #FFF;}
|
||||
.s-btn:hover{background-color: #50aEFF;}
|
||||
|
||||
/* 重置按钮 */
|
||||
.reset-box{text-align: left; font-size: 12px;}
|
||||
.reset-box a{text-decoration: none;}
|
||||
.reset-box a:hover{text-decoration: underline;}
|
||||
|
||||
/* loading框样式 */
|
||||
.ajax-layer-load.layui-layer-dialog{min-width: 0px !important; background-color: rgba(0,0,0,0.85);}
|
||||
.ajax-layer-load.layui-layer-dialog .layui-layer-content{padding: 10px 20px 10px 40px; color: #FFF;}
|
||||
.ajax-layer-load.layui-layer-dialog .layui-layer-content .layui-layer-ico{width: 20px; height: 20px; background-size: 20px 20px; top: 12px; }
|
@@ -1,65 +0,0 @@
|
||||
// sa
|
||||
var sa = {};
|
||||
|
||||
// 打开loading
|
||||
sa.loading = function(msg) {
|
||||
layer.closeAll(); // 开始前先把所有弹窗关了
|
||||
return layer.msg(msg, {icon: 16, shade: 0.3, time: 1000 * 20, skin: 'ajax-layer-load' });
|
||||
};
|
||||
|
||||
// 隐藏loading
|
||||
sa.hideLoading = function() {
|
||||
layer.closeAll();
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------- 登录事件 -----------------------------------
|
||||
|
||||
$('.login-btn').click(function(){
|
||||
sa.loading("正在登录...");
|
||||
// 开始登录
|
||||
setTimeout(function() {
|
||||
$.ajax({
|
||||
url: "sso/doLogin",
|
||||
type: "post",
|
||||
data: {
|
||||
name: $('[name=name]').val(),
|
||||
pwd: $('[name=pwd]').val()
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(res){
|
||||
console.log('返回数据:', res);
|
||||
sa.hideLoading();
|
||||
if(res.code == 200) {
|
||||
layer.msg('登录成功', {anim: 0, icon: 6 });
|
||||
setTimeout(function() {
|
||||
location.reload();
|
||||
}, 800)
|
||||
} else {
|
||||
layer.msg(res.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));
|
||||
}
|
||||
});
|
||||
}, 400);
|
||||
});
|
||||
|
||||
// 绑定回车事件
|
||||
$('[name=name],[name=pwd]').bind('keypress', function(event){
|
||||
if(event.keyCode == "13") {
|
||||
$('.login-btn').click();
|
||||
}
|
||||
});
|
||||
|
||||
// 输入框获取焦点
|
||||
$("[name=name]").focus();
|
||||
|
||||
// 打印信息
|
||||
var str = "This page is provided by Sa-Token, Please refer to: " + "http://sa-token.dev33.cn/";
|
||||
console.log(str);
|
@@ -1,45 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<title>Sa-SSO-Server 认证中心-登录</title>
|
||||
<meta charset="utf-8">
|
||||
<base th:href="@{/}" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<link rel="stylesheet" href="./sa-res/login.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="view-box">
|
||||
<div class="bg-1"></div>
|
||||
<div class="bg-2"></div>
|
||||
<div class="content-box">
|
||||
<div class="login-box">
|
||||
<div class="from-box">
|
||||
<h2 class="from-title">Sa-SSO-Server 认证中心</h2>
|
||||
<div class="from-item">
|
||||
<input class="s-input" name="name" placeholder="请输入账号" />
|
||||
</div>
|
||||
<div class="from-item">
|
||||
<input class="s-input" name="pwd" type="password" placeholder="请输入密码" />
|
||||
</div>
|
||||
<div class="from-item">
|
||||
<button class="s-input s-btn login-btn">登录</button>
|
||||
</div>
|
||||
<div class="from-item reset-box">
|
||||
<a href="javascript: location.reload();" >刷新</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部 版权 -->
|
||||
<div style="position: absolute; bottom: 40px; width: 100%; text-align: center; color: #666;">
|
||||
This page is provided by Sa-Token-SSO
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- scripts -->
|
||||
<script src="./sa-res/jquery.min.js"></script>
|
||||
<script src="./sa-res/layer/layer.js"></script>
|
||||
<script src="./sa-res/login.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
@@ -34,6 +34,7 @@
|
||||
|
||||
- **单点登录**
|
||||
- [单点登录简述](/sso/readme)
|
||||
- [搭建统一认证中心:SSO-Server](/sso/sso-server)
|
||||
- [SSO模式一 共享Cookie同步会话](/sso/sso-type1)
|
||||
- [SSO模式二 URL重定向传播会话](/sso/sso-type2)
|
||||
- [SSO模式三 Http请求获取会话](/sso/sso-type3)
|
||||
|
@@ -18,7 +18,7 @@ public class SsoServerController {
|
||||
return SaSsoHandle.serverRequest();
|
||||
}
|
||||
|
||||
// ... 其它方法
|
||||
// ... 其它代码
|
||||
|
||||
}
|
||||
```
|
||||
|
@@ -80,15 +80,14 @@ if(res.code == 401) {
|
||||
#### 方式二:在配置中配置登录视图地址
|
||||
|
||||
``` java
|
||||
cfg.sso
|
||||
// 配置:未登录时返回的View
|
||||
.setNotLoginView(() -> {
|
||||
cfg.sso.setNotLoginView(() -> {
|
||||
return new ModelAndView("xxx.html");
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
### 3、如何自定义登录API的接口?
|
||||
### 3、如何自定义登录API的接口地址?
|
||||
根据需求点选择解决方案:
|
||||
|
||||
#### 3.1、如果只是想在 setDoLoginHandle 函数里获取除 name、pwd 以外的参数?
|
||||
|
@@ -30,7 +30,7 @@ public class H5Controller {
|
||||
// 根据ticket进行登录
|
||||
@RequestMapping("/doLoginByTicket")
|
||||
public SaResult doLoginByTicket(String ticket) {
|
||||
Object loginId = checkTicket(ticket);
|
||||
Object loginId = SaSsoHandle.checkTicket(ticket);
|
||||
if(loginId != null) {
|
||||
StpUtil.login(loginId);
|
||||
return SaResult.data(StpUtil.getTokenValue());
|
||||
@@ -38,11 +38,6 @@ public class H5Controller {
|
||||
return SaResult.error("无效ticket:" + ticket);
|
||||
}
|
||||
|
||||
// 校验 Ticket码,获取账号Id
|
||||
private Object checkTicket(String ticket) {
|
||||
return SaSsoUtil.checkTicket(ticket);
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
@@ -58,7 +53,7 @@ public class H5Controller {
|
||||
将其复制到项目中即可
|
||||
|
||||
### 3、新建前端项目
|
||||
任意文件夹新建前端项目:`sa-token-demo-sso2-client-h5`,在根目录添加测试文件:`index.html`
|
||||
任意文件夹新建前端项目:`sa-token-demo-sso-client-h5`,在根目录添加测试文件:`index.html`
|
||||
``` js
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@@ -102,43 +97,22 @@ public class H5Controller {
|
||||
```
|
||||
|
||||
### 4、添加登录处理文件`sso-login.html`
|
||||
源码详见:[sso-login.html](https://gitee.com/dromara/sa-token/tree/master/sa-token-demo/sa-token-demo-sso2-client-h5/sso-login.html),
|
||||
源码详见:[sso-login.html](https://gitee.com/dromara/sa-token/tree/master/sa-token-demo/sa-token-demo-sso-client-h5/sso-login.html),
|
||||
将其复制到项目中即可,与`index.html`一样放在根目录下
|
||||
|
||||
|
||||
### 5、测试运行
|
||||
先启动Server服务端与Client服务端,再随便找个能预览html的工具打开前端项目(比如[HBuilderX](https://www.dcloud.io/hbuilderx.html)),测试流程与一体版一致
|
||||
|
||||
### 6、疑问:我在SSO模式三的demo中加入上述代码,提示我 ticket无效,是怎么回事?
|
||||
上述代码是以SSO模式二为基础的,提示“Ticket无效”的原因很简单,因为SSO模式三中 Server端 与 Client端 连接的不是同一个Redis,
|
||||
所以Client端校验Ticket时无法在Redis中查询到相应的值,才会产生异常:“Ticket无效”
|
||||
|
||||
要使上述代码生效很简单,我们只需更改一下校验Ticket的逻辑即可,将 `H5Controller` 中的 `checkTicket` 方法代码改为:
|
||||
``` java
|
||||
// 校验 Ticket码,获取账号Id
|
||||
private Object checkTicket(String ticket) {
|
||||
SaSsoConfig cfg = SaManager.getConfig().getSso();
|
||||
String ssoLogoutCall = null;
|
||||
if(cfg.isSlo) {
|
||||
ssoLogoutCall = SaHolder.getRequest().getUrl().replace("/doLoginByTicket", Api.ssoLogoutCall);
|
||||
}
|
||||
String checkUrl = SaSsoUtil.buildCheckTicketUrl(ticket, ssoLogoutCall);
|
||||
Object body = cfg.sendHttp.apply(checkUrl);
|
||||
return (SaFoxUtil.isEmpty(body) ? null : body);
|
||||
}
|
||||
```
|
||||
|
||||
重新运行项目,即可在SSO模式三中成功整合前后台分离模式 。
|
||||
|
||||
|
||||
### 7、SSO-Server 端的前后台分离
|
||||
### 6、SSO-Server 端的前后台分离
|
||||
疑问:上述代码都是针对 Client 端进行拆分,如果我想在 SSO-Server 端也进行前后台分离改造,应该怎么做?
|
||||
|
||||
> 答:解决思路都是大同小异的,与Client一样,我们需要把原本在 “后端处理的授权重定向逻辑” 拿到前端来实现。
|
||||
|
||||
由于集成代码与 Client 端类似,这里暂不贴详细代码,我们可以下载官方仓库,里面有搭建好的demo
|
||||
|
||||
使用前端ide导入项目 `/sa-token-demo/sa-token-demo-sso2-h5`,浏览器访问 `sso-auth.html` 页面:
|
||||
使用前端ide导入项目 `/sa-token-demo/sa-token-demo-sso-server-h5`,浏览器访问 `sso-auth.html` 页面:
|
||||
|
||||

|
||||
|
||||
@@ -148,7 +122,7 @@ private Object checkTicket(String ticket) {
|
||||
sa-token:
|
||||
sso:
|
||||
# SSO-Server端 统一认证地址
|
||||
auth-url: http://127.0.0.1:8848/sa-token-demo-sso2-server-h5/sso-auth.html
|
||||
auth-url: http://127.0.0.1:8848/sa-token-demo-sso-server-h5/sso-auth.html
|
||||
```
|
||||
|
||||
然后我们启动项目 `sa-token-demo-sso2-server` 与 `sa-token-demo-sso2-client`,按照之前的测试步骤访问:
|
||||
|
297
sa-token-doc/doc/sso/sso-server.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# 搭建统一认证中心 SSO-Server
|
||||
|
||||
在开始SSO三种模式的对接之前,我们必须先搭建一个 SSO-Server 认证中心
|
||||
|
||||
> 搭建示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso-server/`,如遇到难点可结合源码进行测试学习,demo里有制作好的登录页面
|
||||
|
||||
---
|
||||
|
||||
### 1、添加依赖
|
||||
创建 SpringBoot 项目 `sa-token-demo-sso-server`,引入依赖:
|
||||
|
||||
``` xml
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa.top.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa.top.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 视图引擎(在前后端不分离模式下提供视图支持) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Http请求工具(在模式三的单点注销功能下用到,如不需要可以注释掉) -->
|
||||
<dependency>
|
||||
<groupId>com.ejlchina</groupId>
|
||||
<artifactId>okhttps</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
除了 **sa-token-spring-boot-starter** 以外,其它包都是可选的:
|
||||
- 在SSO模式三时 Redis 相关包是可选的
|
||||
- 在前后端分离模式下可以删除 thymeleaf 相关包
|
||||
- 在不需要SSO模式三单点注销的情况下可以删除 http 工具包
|
||||
|
||||
建议先完整测试三种模式之后再对pom依赖进行酌情删减。
|
||||
|
||||
|
||||
### 2、开放认证接口
|
||||
新建 `SsoServerController`,用于对外开放接口:
|
||||
|
||||
``` java
|
||||
/**
|
||||
* Sa-Token-SSO Server端 Controller
|
||||
*/
|
||||
@RestController
|
||||
public class SsoServerController {
|
||||
|
||||
/*
|
||||
* SSO-Server端:处理所有SSO相关请求 (下面的章节我们会详细列出开放的接口)
|
||||
*/
|
||||
@RequestMapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
return SaSsoHandle.serverRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置SSO相关参数
|
||||
*/
|
||||
@Autowired
|
||||
private void configSso(SaTokenConfig cfg) {
|
||||
// 配置:未登录时返回的View
|
||||
cfg.sso.setNotLoginView(() -> {
|
||||
String msg = "当前会话在SSO-Server端尚未登录,请先访问"
|
||||
+ "<a href='/sso/doLogin?name=sa&pwd=123456' target='_blank'> doLogin登录 </a>"
|
||||
+ "进行登录之后,刷新页面开始授权";
|
||||
return msg;
|
||||
});
|
||||
|
||||
// 配置:登录处理函数
|
||||
cfg.sso.setDoLoginHandle((name, pwd) -> {
|
||||
// 此处仅做模拟登录,真实环境应该查询数据进行登录
|
||||
if("sa".equals(name) && "123456".equals(pwd)) {
|
||||
StpUtil.login(10001);
|
||||
return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue());
|
||||
}
|
||||
return SaResult.error("登录失败!");
|
||||
});
|
||||
|
||||
// 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉)
|
||||
cfg.sso.setSendHttp(url -> {
|
||||
return OkHttps.sync(url).get().getBody().toString();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
注:在`setDoLoginHandle`函数里如果要获取name, pwd以外的参数,可通过`SaHolder.getRequest().getParam("xxx")`来获取
|
||||
|
||||
全局异常处理:
|
||||
``` java
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 3、application.yml配置
|
||||
``` yml
|
||||
# 端口
|
||||
server:
|
||||
port: 9000
|
||||
|
||||
# Sa-Token 配置
|
||||
sa-token:
|
||||
# -------------- SSO-模式一相关配置 (非模式一不需要配置)
|
||||
# cookie:
|
||||
# 配置Cookie作用域
|
||||
# domain: stp.com
|
||||
|
||||
# ------- SSO-模式二相关配置
|
||||
sso:
|
||||
# Ticket有效期 (单位: 秒),默认五分钟
|
||||
ticket-timeout: 300
|
||||
# 所有允许的授权回调地址
|
||||
allow-url: "*"
|
||||
# 是否打开单点注销功能
|
||||
is-slo: true
|
||||
|
||||
# ------- SSO-模式三相关配置 (下面的配置在SSO模式三并且 is-slo=true 时打开) -------
|
||||
# 是否打开模式三
|
||||
isHttp: true
|
||||
# 接口调用秘钥(用于SSO模式三的单点注销功能)
|
||||
secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
# ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器(文档有步骤说明)
|
||||
|
||||
spring:
|
||||
# Redis配置 (SSO模式一和模式二使用Redis来同步会话)
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 1
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
```
|
||||
|
||||
注意点:`allow-url`为了方便测试配置为`*`,线上生产环境一定要配置为详细URL地址 (之后的章节我们会详细阐述此配置项)
|
||||
|
||||
|
||||
### 4、创建启动类
|
||||
``` java
|
||||
@SpringBootApplication
|
||||
public class SaSsoServerApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSsoServerApplication.class, args);
|
||||
System.out.println("\n------ Sa-Token-SSO 认证中心启动成功");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
启动项目,不出意外的情况下我们将看到如下输出:
|
||||
|
||||

|
||||
|
||||
访问统一授权地址:
|
||||
- [http://localhost:9000/sso/auth](http://localhost:9000/sso/auth)
|
||||
|
||||

|
||||
|
||||
可以看到这个页面非常简陋,这是因为我们以上的代码示例,主要目标是为了带大家从零搭建一个可用的SSO认证服务端,所以就对一些不太必要的步骤做了简化
|
||||
|
||||
大家可以下载运行一下官方仓库里的示例`/sa-token-demo/sa-token-demo-sso-server/`,里面有制作好的登录页面:
|
||||
|
||||

|
||||
|
||||
默认账号密码为:`sa / 123456`,大家先别着急点击登录,因为我们还没有搭建对应的 Client 端项目,
|
||||
真实项目中我们是不会直接从浏览器访问 `/sso/auth` 授权地址的,我们需要在 Client 端点击登录按钮重定向而来。
|
||||
|
||||
现在我们先来看看除了 `/sso/auth` 统一授权地址,这个 SSO-Server 认证中心还开放了哪些API呢,且往下看
|
||||
|
||||
### 5、API 列表
|
||||
|
||||
如果你仅仅使用 Sa-Token 搭建 SSO-Server 端,而 Client 端使用其它框架的话,那么下面的 API 列表将给你的对接步骤做一份参考。
|
||||
|
||||
如果你在 Client 端也用到了 Sa-Token 框架,那么你可以选择跳过本小节,Sa-Token 对 Client 端也提供了相应的封装,你可以直接开始学习:[SSO模式一 共享Cookie同步会话](/sso/sso-type1)
|
||||
|
||||
|
||||
#### 5.1、单点登录授权地址
|
||||
``` url
|
||||
http://{host}:{port}/sso/auth
|
||||
```
|
||||
|
||||
接收参数:
|
||||
|
||||
| 参数 | 是否必填 | 说明 |
|
||||
| :-------- | :-------- | :-------- |
|
||||
| redirect | 是 | 登录成功后的重定向地址,一般填写 location.href(从哪来回哪去) |
|
||||
| mode | 否 | 授权模式,取值 [simple, ticket],simple=登录后直接重定向,ticket=带着ticket参数重定向,默认值为ticket |
|
||||
|
||||
访问接口后有两种情况:
|
||||
- 情况一:当前会话在 SSO 认证中心未登录,会进入登录页开始登录。
|
||||
- 情况二:当前会话在 SSO 认证中心已登录,会被重定向至 `redirect` 地址,并携带 `ticket` 参数。
|
||||
|
||||
|
||||
#### 5.2、RestAPI 登录接口
|
||||
``` url
|
||||
http://{host}:{port}/sso/doLogin
|
||||
```
|
||||
|
||||
接收参数:
|
||||
|
||||
| 参数 | 是否必填 | 说明 |
|
||||
| :-------- | :-------- | :-------- |
|
||||
| name | 是 | 用户名 |
|
||||
| pwd | 是 | 密码 |
|
||||
|
||||
此接口属于 RestAPI (使用ajax访问),会进入后端配置的 `setDoLoginHandle` 函数中,另外需要注意:
|
||||
此接口并非只能携带 name、pwd 参数,因为你可以在 setDoLoginHandle 函数里通过 `SaHolder.getRequest().getParam("xxx")` 来获取其它参数。
|
||||
|
||||
|
||||
#### 5.3、Ticket 校验接口
|
||||
此接口仅配置模式三 `(isHttp=true)` 时打开
|
||||
|
||||
``` url
|
||||
http://{host}:{port}/sso/checkTicket
|
||||
```
|
||||
|
||||
接收参数:
|
||||
|
||||
| 参数 | 是否必填 | 说明 |
|
||||
| :-------- | :-------- | :-------- |
|
||||
| ticket | 是 | 在步骤 5.1 中授权重定向时的 ticket 参数 |
|
||||
| ssoLogoutCall | 否 | 单点注销时的回调通知地址,只在SSO模式三时需要携带此参数|
|
||||
|
||||
返回值场景:
|
||||
- 返回空,代表校验失败。
|
||||
- 返回具体的 loginId,例如10001,代表校验成功,值为此 ticket 码代表的用户id。
|
||||
|
||||
|
||||
#### 5.4、单点注销接口
|
||||
``` url
|
||||
http://{host}:{port}/sso/logout
|
||||
```
|
||||
|
||||
接受参数:
|
||||
|
||||
| 参数 | 是否必填 | 说明 |
|
||||
| :-------- | :-------- | :-------- |
|
||||
| loginId | 否 | 要注销的账号id |
|
||||
| secretkey | 否 | 接口通信秘钥 |
|
||||
| back | 否 | 注销成功后的重定向地址 |
|
||||
|
||||
|
||||
此接口有两种调用方式
|
||||
|
||||
##### 方式一:在前端页面引导用户直接跳转,并带有 back 参数
|
||||
例如:`http://{host}:{port}/sso/logout?back=xxx`,代表用户注销成功后返回back地址
|
||||
|
||||
##### 方式二:在 Client 的后端通过 http 工具来调用
|
||||
例如:`http://{host}:{port}/sso/logout?loginId={value}&secretkey={value}`,代表注销 账号=loginId 的账号,返回json数据结果,形如:
|
||||
|
||||
``` js
|
||||
{
|
||||
"code": 200, // 200表示请求成功,非200标识请求失败
|
||||
"msg": "单点注销成功",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
SSO 认证中心只有这四个接口,接下来让我一起来看一下 Client 端的对接流程:[SSO模式一 共享Cookie同步会话](/sso/sso-type1)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
### 0、解决思路?
|
||||
### 1、解决思路?
|
||||
|
||||
首先我们分析一下多个系统之间,为什么无法同步登录状态?
|
||||
1. 前端的 `Token` 无法在多个系统下共享。
|
||||
@@ -20,12 +20,8 @@
|
||||
|
||||
OK,所有理论就绪,下面开始实战:
|
||||
|
||||
> Sa-Token整合同域单点登录非常简单,相比于正常的登录,你只需增加配置 `sa-token.cookie-domain=xxx.com` 指定一下Cookie写入时的父级域名即可。 <br>
|
||||
> 整合示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso1/`,如遇到难点可结合源码进行测试学习。
|
||||
|
||||
|
||||
|
||||
### 1、准备工作
|
||||
### 2、准备工作
|
||||
|
||||
首先修改hosts文件`(C:\windows\system32\drivers\etc\hosts)`,添加以下IP映射,方便我们进行测试:
|
||||
``` url
|
||||
@@ -35,109 +31,153 @@ OK,所有理论就绪,下面开始实战:
|
||||
127.0.0.1 s3.stp.com
|
||||
```
|
||||
|
||||
<!-- 其中:`sso.stp.com`为统一认证地址,当用户在其它 Client 端发起登录请求时,均将其重定向至认证中心,待到登录成功之后再原路返回到 Client 端。 -->
|
||||
其中:`sso.stp.com`为统一认证地址,其它均为 Client 端。
|
||||
其中:`sso.stp.com`为统一认证中心地址,当用户在其它 Client 端发起登录请求时,均将其重定向至认证中心,待到登录成功之后再原路返回到 Client 端。
|
||||
|
||||
|
||||
### 2、指定Cookie的作用域
|
||||
在`s1.stp.com`访问服务器,其Cookie也只能写入到`s1.stp.com`下,为了将Cookie写入到其父级域名`stp.com`下,我们需要新增配置:
|
||||
### 3、指定Cookie的作用域
|
||||
在`sso.stp.com`访问服务器,其Cookie也只能写入到`sso.stp.com`下,为了将Cookie写入到其父级域名`stp.com`下,我们需要更改 SSO-Server 端的 yml 配置:
|
||||
|
||||
``` yml
|
||||
sa-token:
|
||||
# 写入Cookie时显式指定的作用域, 用于单点登录二级域名共享Cookie
|
||||
cookie-domain: stp.com
|
||||
cookie:
|
||||
# 配置Cookie作用域 (这个配置原本是被注释掉的,现在我们将其打开)
|
||||
domain: stp.com
|
||||
```
|
||||
|
||||
### 3、新增测试Controller
|
||||
新建`SsoController.java`控制器,写入代码:
|
||||
注:在SSO模式一测试完毕之后,一定要将这个配置再次注释掉,因为模式一与模式二三使用不同的授权流程,这行配置会影响到我们模式二和模式三的正常运行。
|
||||
|
||||
|
||||
|
||||
### 4、搭建 Client 端项目
|
||||
|
||||
> 整合示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso1-client/`,如遇到难点可结合源码进行测试学习。
|
||||
|
||||
|
||||
#### 4.1、引入依赖
|
||||
新建项目 sa-token-demo-sso1-client,并添加以下依赖:
|
||||
|
||||
``` xml
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token插件:权限缓存与业务缓存分离 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-alone-redis</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
#### 4.2、新建 Controller 控制器
|
||||
|
||||
``` java
|
||||
/**
|
||||
* 测试: 同域单点登录
|
||||
* Sa-Token-SSO Client端 Controller
|
||||
* @author kong
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/sso/")
|
||||
public class SsoController {
|
||||
public class SsoClientController {
|
||||
|
||||
// 测试:进行登录
|
||||
@RequestMapping("doLogin")
|
||||
public SaResult doLogin(@RequestParam(defaultValue = "10001") String id) {
|
||||
System.out.println("---------------- 进行登录 ");
|
||||
StpUtil.login(id);
|
||||
return SaResult.ok("登录成功: " + id);
|
||||
// SSO-Client端:首页
|
||||
@RequestMapping("/")
|
||||
public String index() {
|
||||
String authUrl = SaManager.getConfig().getSso().getAuthUrl();
|
||||
String solUrl = SaManager.getConfig().getSso().getSloUrl();
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
|
||||
"<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" +
|
||||
"<p><a href=\"javascript:location.href='" + authUrl + "?mode=simple&redirect=' + encodeURIComponent(location.href);\">登录</a> " +
|
||||
"<a href=\"javascript:location.href='" + solUrl + "?back=' + encodeURIComponent(location.href);\">注销</a> </p>";
|
||||
return str;
|
||||
}
|
||||
|
||||
// 测试:是否登录
|
||||
@RequestMapping("isLogin")
|
||||
public SaResult isLogin() {
|
||||
System.out.println("---------------- 是否登录 ");
|
||||
boolean isLogin = StpUtil.isLogin();
|
||||
return SaResult.ok("是否登录: " + isLogin);
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.3、application.yml 配置
|
||||
|
||||
``` yml
|
||||
# 端口
|
||||
server:
|
||||
port: 9001
|
||||
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso:
|
||||
# SSO-Server端-单点登录授权地址
|
||||
auth-url: http://sso.stp.com:9000/sso/auth
|
||||
# SSO-Server端-单点注销地址
|
||||
slo-url: http://sso.stp.com:9000/sso/logout
|
||||
|
||||
# 配置Sa-Token单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis)
|
||||
alone-redis:
|
||||
# Redis数据库索引
|
||||
database: 1
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
```
|
||||
|
||||
#### 4.4、启动类
|
||||
|
||||
``` java
|
||||
// 启动类
|
||||
/**
|
||||
* SSO模式一,Client端 Demo
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaSsoApplication {
|
||||
public class SaSsoClientApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSsoApplication.class, args);
|
||||
System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
|
||||
SpringApplication.run(SaSsoClientApplication.class, args);
|
||||
System.out.println("\nSa-Token SSO模式一 Client端启动成功");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 4、访问测试
|
||||
启动项目,依次访问:
|
||||
- [http://s1.stp.com:8081/sso/isLogin](http://s1.stp.com:8081/sso/isLogin)
|
||||
- [http://s2.stp.com:8081/sso/isLogin](http://s2.stp.com:8081/sso/isLogin)
|
||||
- [http://s3.stp.com:8081/sso/isLogin](http://s3.stp.com:8081/sso/isLogin)
|
||||
|
||||
均返回以下结果:
|
||||
|
||||

|
||||
|
||||
现在访问SSO认证中心的登录接口:[http://sso.stp.com:8081/sso/doLogin](http://sso.stp.com:8081/sso/doLogin)
|
||||
|
||||

|
||||
|
||||
然后再次刷新上面三个测试接口,均可以得到以下结果:
|
||||
|
||||

|
||||
|
||||
测试完毕!
|
||||
|
||||
|
||||
### 5、完善统一认证中心
|
||||
|
||||
上面的示例,我们简单的演示了SSO模式一的认证原理。
|
||||
|
||||
当然,在实际的正式项目中,我们肯定不会每个 Client 端都内置一个登录接口,一般的做法是只在SSO认证中心保留登录接口,我们所有 Client 端的登录请求都会被重定向至认证中心,
|
||||
待到登录成功之后再原路返回到 Client 端。
|
||||
|
||||
我们可以运行一下官方仓库的示例,里面有制作好的登录页面
|
||||
|
||||
> 下载官方示例,依次运行:
|
||||
> - `/sa-token-demo/sa-token-demo-sso1-server/`
|
||||
> - `/sa-token-demo/sa-token-demo-sso1-client/`
|
||||
|
||||
|
||||
访问三个应用端:
|
||||
### 5、访问测试
|
||||
启动项目,依次访问三个应用端:
|
||||
- [http://s1.stp.com:9001/](http://s1.stp.com:9001/)
|
||||
- [http://s2.stp.com:9001/](http://s2.stp.com:9001/)
|
||||
- [http://s3.stp.com:9001/](http://s3.stp.com:9001/)
|
||||
|
||||
|
||||
均返回:
|
||||
|
||||

|
||||
|
||||
然后点击登录,被重定向至SSO认证中心:
|
||||
|
||||

|
||||

|
||||
|
||||
输入默认测试账号:`sa / 123456`,点击登录
|
||||
我们点击登录,然后刷新页面:
|
||||
|
||||

|
||||
|
||||
@@ -148,9 +188,10 @@ public class SaSsoApplication {
|
||||
测试完成
|
||||
|
||||
|
||||
|
||||
### 6、跨域模式下的解决方案
|
||||
|
||||
如上,我们使用极其简单的步骤实现了同域下的单点登录,聪明如你😏,马上想到了这种模式有着一个不小的限制:
|
||||
如上,我们使用简单的步骤实现了同域下的单点登录,聪明如你😏,马上想到了这种模式有着一个不小的限制:
|
||||
|
||||
> 所有子系统的域名,必须同属一个父级域名
|
||||
|
||||
@@ -161,3 +202,4 @@ public class SaSsoApplication {
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
如果我们的多个系统:部署在不同的域名之下,但是后端可以连接同一个Redis,那么便可以使用 **`[URL重定向传播会话]`** 的方式做到单点登录。
|
||||
|
||||
|
||||
### 0、解题思路
|
||||
### 1、解题思路
|
||||
|
||||
首先我们再次复习一下,多个系统之间为什么无法同步登录状态?
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
下面我们按照步骤依次完成上述过程:
|
||||
|
||||
### 1、准备工作
|
||||
### 2、准备工作
|
||||
首先修改hosts文件`(C:\windows\system32\drivers\etc\hosts)`,添加以下IP映射,方便我们进行测试:
|
||||
``` url
|
||||
127.0.0.1 sa-sso-server.com
|
||||
@@ -39,134 +39,24 @@
|
||||
127.0.0.1 sa-sso-client3.com
|
||||
```
|
||||
|
||||
|
||||
### 2、搭建 SSO-Server 认证中心
|
||||
|
||||
> 搭建示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso2-server/`,如遇到难点可结合源码进行测试学习
|
||||
|
||||
#### 2.1、创建 SSO-Server 端项目
|
||||
创建 SpringBoot 项目 `sa-token-demo-sso-server`,引入依赖:
|
||||
|
||||
``` xml
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa.top.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa.top.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### 2.2、开放认证接口
|
||||
一个完整的SSO认证中心应该至少包含以下接口:
|
||||
- `/sso/auth`:单点登录统一认证地址。
|
||||
- `/sso/doLogin`:RestAPI 登录接口,根据账号密码进行登录。
|
||||
- `/sso/logout`:统一单点注销地址,一次注销,全端下线。
|
||||
|
||||
别急,这里不需要你亲自完成这些接口 —— Sa-Token 已经为你封装了实现。你要做的,就是提供一个访问入口,接入 Sa-Token 的方法。
|
||||
|
||||
``` java
|
||||
/**
|
||||
* Sa-Token-SSO Server端 Controller
|
||||
*/
|
||||
@RestController
|
||||
public class SsoServerController {
|
||||
|
||||
/*
|
||||
* SSO-Server端:处理所有SSO相关请求
|
||||
* http://{host}:{port}/sso/auth -- 单点登录授权地址,接受参数:redirect=授权重定向地址
|
||||
* http://{host}:{port}/sso/doLogin -- 账号密码登录接口,接受参数:name、pwd
|
||||
* http://{host}:{port}/sso/checkTicket -- Ticket校验接口(isHttp=true时打开),接受参数:ticket=ticket码、ssoLogoutCall=单点注销回调地址 [可选]
|
||||
* http://{host}:{port}/sso/logout -- 单点注销地址(isSlo=true时打开),接受参数:loginId=账号id、secretkey=接口调用秘钥
|
||||
*/
|
||||
@RequestMapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
return SaSsoHandle.serverRequest();
|
||||
}
|
||||
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaTokenConfig cfg) {
|
||||
cfg.sso
|
||||
// 配置:未登录时返回的View
|
||||
.setNotLoginView(() -> {
|
||||
String msg = "当前会话在SSO-Server端尚未登录,请先访问"
|
||||
+ "<a href='/sso/doLogin?name=sa&pwd=123456' target='_blank'> doLogin登录 </a>"
|
||||
+ "进行登录之后,刷新页面开始授权";
|
||||
return msg;
|
||||
})
|
||||
// 配置:登录处理函数
|
||||
.setDoLoginHandle((name, pwd) -> {
|
||||
// 此处仅做模拟登录,真实环境应该查询数据进行登录
|
||||
if("sa".equals(name) && "123456".equals(pwd)) {
|
||||
StpUtil.login(10001);
|
||||
return SaResult.ok("登录成功!");
|
||||
}
|
||||
return SaResult.error("登录失败!");
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
注意:在`setDoLoginHandle`函数里如果要获取name, pwd以外的参数,可通过`SaHolder.getRequest().getParam("xxx")`来获取
|
||||
|
||||
#### 2.3、application.yml配置
|
||||
``` yml
|
||||
# 端口
|
||||
server:
|
||||
port: 9000
|
||||
|
||||
# Sa-Token配置
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso:
|
||||
# Ticket有效期 (单位: 秒),默认五分钟
|
||||
ticket-timeout: 300
|
||||
# 所有允许的授权回调地址 (此处为了方便测试配置为*,线上生产环境一定要配置为详细地地址)
|
||||
allow-url: "*"
|
||||
|
||||
spring:
|
||||
# Redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 1
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
```
|
||||
注意点:`allow-url`为了方便测试配置为`*`,线上生产环境一定要配置为详细URL地址 (之后的章节我们会详细阐述此配置项)
|
||||
|
||||
#### 2.4、创建SSO-Server端启动类
|
||||
``` java
|
||||
@SpringBootApplication
|
||||
public class SaSsoServerApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSsoServerApplication.class, args);
|
||||
System.out.println("\nSa-Token-SSO 认证中心启动成功");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 3、搭建 SSO-Client 应用端
|
||||
### 3、搭建 Client 端项目
|
||||
|
||||
> 搭建示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso2-client/`,如遇到难点可结合源码进行测试学习
|
||||
|
||||
#### 3.1、创建SSO-Client端项目
|
||||
#### 3.1、去除 SSO-Server 的 Cookie 作用域配置
|
||||
在SSO模式一章节中我们打开了配置:
|
||||
|
||||
``` yml
|
||||
sa-token:
|
||||
cookie:
|
||||
# 配置Cookie作用域
|
||||
domain: stp.com
|
||||
```
|
||||
|
||||
此为模式一专属配置,现在我们将其注释掉,并按照注释提示打开其他相应的注释
|
||||
|
||||
|
||||
#### 3.2、创建 SSO-Client 端项目
|
||||
创建一个 SpringBoot 项目 `sa-token-demo-sso-client`,引入依赖:
|
||||
``` xml
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
@@ -196,7 +86,7 @@ public class SaSsoServerApplication {
|
||||
```
|
||||
|
||||
|
||||
#### 3.2、创建 SSO-Client 端认证接口
|
||||
#### 3.3、创建 SSO-Client 端认证接口
|
||||
|
||||
同 SSO-Server 一样,Sa-Token 为 SSO-Client 端所需代码也提供了完整的封装,你只需提供一个访问入口,接入 Sa-Token 的方法即可。
|
||||
|
||||
@@ -232,7 +122,7 @@ public class SsoClientController {
|
||||
}
|
||||
```
|
||||
|
||||
##### 3.3、配置SSO认证中心地址
|
||||
##### 3.4、配置SSO认证中心地址
|
||||
你需要在 `application.yml` 配置如下信息:
|
||||
``` yml
|
||||
# 端口
|
||||
@@ -261,7 +151,7 @@ sa-token:
|
||||
```
|
||||
注意点:`sa-token.alone-redis` 的配置需要和SSO-Server端连接同一个Redis(database也要一样)
|
||||
|
||||
#### 3.4、写启动类
|
||||
#### 3.5、写启动类
|
||||
``` java
|
||||
@SpringBootApplication
|
||||
public class SaSsoClientApplication {
|
||||
@@ -314,7 +204,7 @@ public class SaSsoClientApplication {
|
||||
|
||||

|
||||
|
||||
|
||||
<!--
|
||||
### 5、运行官方仓库
|
||||
|
||||
以上示例,虽然完整的复现了单点登录的过程,但是页面还是有些简陋,我们可以运行一下官方仓库的示例,里面有制作好的登录页面
|
||||
@@ -329,30 +219,16 @@ public class SaSsoClientApplication {
|
||||

|
||||
|
||||
默认测试密码:`sa / 123456`,其余流程保持不变
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
### 6、跨Redis的单点登录
|
||||
### 5、跨 Redis 的单点登录
|
||||
以上流程解决了跨域模式下的单点登录,但是后端仍然采用了共享Redis来同步会话,如果我们的架构设计中Client端与Server端无法共享Redis,又该怎么完成单点登录?
|
||||
|
||||
这就要采用模式三了,且往下看:[SSO模式三:Http请求获取会话](/sso/sso-type3)
|
||||
|
||||
|
||||
<!--
|
||||
### 6、如何单点注销?
|
||||
由于Server端与所有Client端都是在共用同一套会话,因此只要一端注销,即可全端下线,达到单点注销的效果
|
||||
|
||||
在`SsoClientController`中添加以下代码:
|
||||
``` java
|
||||
// SSO-Client端:单点注销 (所有端一起下线)
|
||||
@RequestMapping("logout")
|
||||
public SaResult logout() {
|
||||
StpUtil.logout();
|
||||
return SaResult.ok();
|
||||
}
|
||||
``` -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -5,7 +5,7 @@
|
||||
> 阅读本篇之前请务必先熟读SSO模式二!因为模式三仅仅属于模式二的一个特殊场景,熟读模式二有助于您快速理解本章内容
|
||||
|
||||
|
||||
### 0、问题分析
|
||||
### 1、问题分析
|
||||
我们先来分析一下,当后端不使用共享 Redis 时,会对架构产生哪些影响:
|
||||
|
||||
1. Client 端无法直连 Redis 校验 ticket,取出账号id。
|
||||
@@ -14,19 +14,13 @@
|
||||
|
||||
所以模式三的主要目标:也就是在 模式二的基础上 解决上述 三个难题
|
||||
|
||||
> 模式三的 Demo 示例地址:
|
||||
>
|
||||
> - SSO-Server 端:`/sa-token-demo/sa-token-demo-sso3-server/` [源码链接](https://gitee.com/dromara/sa-token/tree/dev/sa-token-demo/sa-token-demo-sso3-server) <br/>
|
||||
> - SSO-Client 端:`/sa-token-demo/sa-token-demo-sso3-client/` [源码链接](https://gitee.com/dromara/sa-token/tree/dev/sa-token-demo/sa-token-demo-sso3-client) <br/>
|
||||
>
|
||||
> 如遇难点可参考示例
|
||||
> 模式三的 Demo 示例地址:`/sa-token-demo/sa-token-demo-sso3-client/`
|
||||
> [源码链接](https://gitee.com/dromara/sa-token/tree/dev/sa-token-demo/sa-token-demo-sso3-client),如遇难点可参考示例
|
||||
|
||||
|
||||
### 1、SSO-Server 认证中心开放 Ticket 校验接口
|
||||
既然 Client 端无法直连 Redis 校验 Ticket,那我们就在 Server 端开放 Ticket 校验接口,然后 Client 端通过 http 请求获取数据。
|
||||
### 2、在Client 端更改 Ticket 校验方式
|
||||
|
||||
#### 1.1、添加依赖
|
||||
首先在 Server 端和 Client 端均添加以下依赖(如果不需要单点注销功能则 Server 端可不引入)
|
||||
#### 2.1、增加 pom.xml 配置
|
||||
``` xml
|
||||
<!-- Http请求工具 -->
|
||||
<dependency>
|
||||
@@ -37,49 +31,42 @@
|
||||
```
|
||||
> OkHttps是一个轻量级http请求工具,详情参考:[OkHttps](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
|
||||
#### 1.2、认证中心开放接口
|
||||
在 SSO-Server 端的 `application.yml` 中,新增以下配置:
|
||||
``` yml
|
||||
sa-token:
|
||||
sso:
|
||||
# 使用Http请求校验ticket
|
||||
is-http: true
|
||||
```
|
||||
此配置项的作用是开放ticket校验接口,让Client端通过http请求获取会话
|
||||
|
||||
#### 1.3、Client端新增配置
|
||||
#### 2.2、配置 http 请求处理器
|
||||
在SSO-Client端的 `SsoClientController` 中,新增以下配置
|
||||
``` java
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaTokenConfig cfg) {
|
||||
cfg.sso
|
||||
// ... 其他代码
|
||||
|
||||
// 配置 Http 请求处理器
|
||||
.setSendHttp(url -> {
|
||||
cfg.sso.setSendHttp(url -> {
|
||||
return OkHttps.sync(url).get().getBody().toString();
|
||||
})
|
||||
;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3、application.yml 新增配置
|
||||
``` yml
|
||||
sa-token:
|
||||
sso:
|
||||
# 使用Http请求校验ticket
|
||||
# 打开模式三(使用Http请求校验ticket)
|
||||
is-http: true
|
||||
# SSO-Server端 ticket校验地址
|
||||
check-ticket-url: http://sa-sso-server.com:9000/sso/checkTicket
|
||||
```
|
||||
|
||||
#### 1.4、启动项目测试
|
||||
启动SSO-Server、SSO-Client,访问测试:[http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/)
|
||||
#### 2.4、启动项目测试
|
||||
重启项目,访问测试:[http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/)
|
||||
> 注:如果已测试运行模式二,可先将Redis中的数据清空,以防旧数据对测试造成干扰
|
||||
|
||||
|
||||
### 2、获取 Userinfo
|
||||
除了账号id,我们可能还需要将用户的昵称、头像等信息从 Server端 带到 Client端,即:用户资料的同步。要解决这个需求,我们只需:
|
||||
### 3、获取 Userinfo
|
||||
除了账号id,我们可能还需要将用户的昵称、头像等信息从 Server端 带到 Client端,即:用户资料的同步。
|
||||
|
||||
#### 2.1、在 Server 端自定义接口,查询用户资料
|
||||
在模式二中我们只需要将需要同步的资料放到 SaSession 即可,但是在模式三中两端不再连接同一个Redis,这时候我们需要通过http接口来同步信息:
|
||||
|
||||
#### 3.1、在 Server 端自定义接口,查询用户资料
|
||||
``` java
|
||||
// 自定义接口:获取userinfo
|
||||
@RequestMapping("/sso/userinfo")
|
||||
@@ -98,7 +85,7 @@ public Object userinfo(String loginId, String secretkey) {
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2、在 Client 端调用此接口查询 userinfo
|
||||
#### 3.2、在 Client 端调用此接口查询 userinfo
|
||||
首先在yml中配置接口地址
|
||||
``` yml
|
||||
sa-token:
|
||||
@@ -122,7 +109,7 @@ public Object myinfo() {
|
||||
|
||||
|
||||
|
||||
### 3、无刷单点注销
|
||||
### 4、无刷单点注销
|
||||
|
||||
有了单点登录就必然要有单点注销,网上给出的大多数解决方案是将注销请求重定向至SSO-Server中心,逐个通知Client端下线
|
||||
|
||||
@@ -137,34 +124,7 @@ public Object myinfo() {
|
||||
|
||||
这些逻辑 Sa-Token 内部已经封装完毕,你只需按照文章增加以下配置即可:
|
||||
|
||||
#### 2.1、SSO-Server认证中心增加配置
|
||||
在 `SsoServerController` 中新增配置
|
||||
``` java
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaTokenConfig cfg) {
|
||||
cfg.sso
|
||||
// ... (其它配置保持不变)
|
||||
// 配置Http请求处理器
|
||||
.setSendHttp(url -> {
|
||||
// 此处为了提高响应速度这里可将sync换为async
|
||||
return OkHttps.sync(url).get();
|
||||
})
|
||||
;
|
||||
}
|
||||
```
|
||||
|
||||
并在 `application.yml` 下新增配置:
|
||||
``` yml
|
||||
sa-token:
|
||||
sso:
|
||||
# 打开单点注销功能
|
||||
is-slo: true
|
||||
# API调用秘钥(用于SSO模式三的单点注销功能)
|
||||
secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
```
|
||||
|
||||
#### 2.2、SSO-Client 端新增配置
|
||||
#### 4.1、SSO-Client 端新增配置
|
||||
|
||||
在 `application.yml` 增加配置:`API调用秘钥` 和 `单点注销接口URL`。
|
||||
``` yml
|
||||
@@ -177,9 +137,11 @@ sa-token:
|
||||
# 接口调用秘钥
|
||||
secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
```
|
||||
注意 secretkey 秘钥需要与SSO认证中心的一致
|
||||
|
||||
#### 2.3 启动测试
|
||||
启动SSO-Server、SSO-Client,访问测试:[http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/),
|
||||
|
||||
#### 4.2 启动测试
|
||||
重启项目,访问测试:[http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/),
|
||||
我们主要的测试点在于 `单点注销`,正常登录即可。
|
||||
|
||||

|
||||
|
@@ -108,15 +108,12 @@ implementation 'cn.dev33:sa-token-core:${sa.top.version}'
|
||||
├── sa-token-demo-solon // [示例] Sa-Token 集成 Solon
|
||||
├── sa-token-demo-quick-login // [示例] Sa-Token 集成 quick-login 模块
|
||||
├── sa-token-demo-alone-redis // [示例] Sa-Token 集成 alone-redis 模块
|
||||
├── sa-token-demo-sso1 // [示例] Sa-Token 集成 SSO单点登录-模式一简单测试
|
||||
├── sa-token-demo-sso1-server // [示例] Sa-Token 集成 SSO单点登录-模式一 认证中心
|
||||
├── sa-token-demo-sso-server // [示例] Sa-Token 集成 SSO单点登录-Server认证中心
|
||||
├── sa-token-demo-sso1-client // [示例] Sa-Token 集成 SSO单点登录-模式一 应用端
|
||||
├── sa-token-demo-sso2-server // [示例] Sa-Token 集成 SSO单点登录-模式二 认证中心
|
||||
├── sa-token-demo-sso2-client-h5 // [示例] Sa-Token 集成 SSO单点登录-模式二 应用端
|
||||
├── sa-token-demo-sso2-server // [示例] Sa-Token 集成 SSO单点登录-模式二 认证中心 (前后端分离)
|
||||
├── sa-token-demo-sso2-client-h5 // [示例] Sa-Token 集成 SSO单点登录-模式二 应用端 (前后端分离)
|
||||
├── sa-token-demo-sso3-server // [示例] Sa-Token 集成 SSO单点登录-模式三 认证中心
|
||||
├── sa-token-demo-sso2-client // [示例] Sa-Token 集成 SSO单点登录-模式二 应用端
|
||||
├── sa-token-demo-sso3-client // [示例] Sa-Token 集成 SSO单点登录-模式三 应用端
|
||||
├── sa-token-demo-sso-server-h5 // [示例] Sa-Token 集成 SSO单点登录-Server认证中心 (前后端分离)
|
||||
├── sa-token-demo-sso-client-h5 // [示例] Sa-Token 集成 SSO单点登录-client应用端 (前后端分离)
|
||||
├── sa-token-demo-oauth2-server // [示例] Sa-Token 集成 OAuth2.0 (服务端)
|
||||
├── sa-token-demo-oauth2-client // [示例] Sa-Token 集成 OAuth2.0 (客户端)
|
||||
├── sa-token-doc // [文档] Sa-Token 开发文档
|
||||
|