单点登录云平台子系统集成方式
后台集成:
1. 在pom中添加单点登录客户端支持
cn.dev33
sa-token-spring-boot-starter
1.31.0
cn.dev33
sa-token-sso
1.31.0
cn.dev33
sa-token-dao-redis
1.31.0
org.apache.commons
commons-pool2
com.alibaba
fastjson
1.2.4
2. 修改application.yml 添加如下内容
spring:
# redis配置 如果框架在集成前有连接则无需添加,如没有则加入
redis:
# Redis数据库索引(默认为0)
database: 1
# Redis服务器地址
host: 49.235.237.65
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password: '!1qaz@2WSX'
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
# sa-token配置
sa-token:
token-name: X-Token
# SSO-相关配置
sso:
# SSO-Server端 统一认证地址
auth-url: http://114.116.5.89:8081/ltCloud_manage/sso/auth
# 是否打开单点注销接口
is-slo: true
# 配置Sa-Token单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis)
alone-redis:
# Redis数据库索引 (默认为0)
database: 1
# Redis服务器地址
host: 49.235.237.65
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password: '!1qaz@2WSX'
3. 将SsoClientController.java放入Controller包下。
将CommonExceptionHandler.java覆盖到common.exceptionHandler包的同名文件。
将LoginInterceptor.java 覆盖到interceptor包下
将SaTokenConfigure.java拷贝至interceptor包下
4. 检查SpringBootServletInitializer类的buildConfig方法,AllowCredentials值要为true,如下图:
前台集成
1. 所有请求的header里添加client-url参数,值为location.href(当前地址栏),如下图:
添加后的结果如下图:
2. 判断所有后台响应的状态码 为50102或50101(两个状态码都说明登录异常),
如出现此情况,将浏览器重定向至返回参数中data. clientLoginUrl的地址,程序会跳转至sso进行登录验证,并自动返回至当前页面。
注意事项:
1. 该方案为临时方案,需集成至本司框架下。
2. 使用时需先在http://114.116.5.89:8081/cloudmanage登录,登录后即可将子项目登录成功。
3. 子项目登录成功后,可以通过StpUtil.getLoginId()的静态方法获得登录用户id
4. 请保持网络连接;请保持自己开发的项目前后端在同一个域,但可以不同端口(例如,前端是http://localhost:8080,后端是http://localhost:8081,但不可以是前端http://localhost:8080,后端http://127.0.0.1:8081)
所需文件:
CommonExceptionHandler.java:
package com.todod.ltcloud.manage.common.exceptionHandler;
import java.util.List;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.util.SaFoxUtil;
import com.alibaba.fastjson.JSONObject;
import com.todod.ltcloud.manage.exception.BusinessException;
import com.todod.ltcloud.manage.exception.LoginExpiredException;
import com.todod.ltcloud.manage.exception.LoginOutException;
import com.todod.ltcloud.manage.exception.SingleLoginException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestClientException;
/**
* 统一异常处理
*
* @author gsh
* @version 创建时间:2018年11月30日 下午5:25:35
*/
@ControllerAdvice
public class CommonExceptionHandler {
Logger log = LoggerFactory.getLogger(this.getClass());
/**
* 参数校验失败
*
* @param ex
* @return
* @Title: handleBindException
* @Description:
* @author gsh
* @date 2019年8月13日
*/
@ExceptionHandler(BindException.class)
@ResponseBody
public ResultData handleBindException(BindException ex) {
// ex.getFieldError():随机返回一个对象属性的异常信息。如果要一次性返回所有对象属性异常信息,则调用ex.getAllErrors();
FieldError fieldError = ex.getFieldError();
log.warn("参数校验失败异常:-----------------" + fieldError.getDefaultMessage());
return ResultData.error(ResultData.PARAM_ERROR_CODE, fieldError.getDefaultMessage());
}
/**
* 参数校验失败
*
* @param ex
* @return
* @Title: handleBindException
* @Description:
* @author gsh
* @date 2020年7月15日
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ResultData handleBindException(MethodArgumentNotValidException ex) {
List errors = ex.getBindingResult().getAllErrors();
String message = "参数不合法";
if (errors.size() > 0) {
message = errors.get(0).getDefaultMessage();
}
log.warn("参数校验失败异常:-----------------" + message);
return ResultData.error(ResultData.PARAM_ERROR_CODE, message);
}
/**
* 请求方式不正确
*
* @param e
* @return
* @Title: exceptionHandler
* @Description:
* @author gsh
* @date 2019年10月14日
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseBody
public ResultData exceptionHandler(HttpRequestMethodNotSupportedException e) {
log.warn("请求方式不正确", e.getMessage());
return ResultData.error(ResultData.METHOD_NOT_SUPPORTED, e.getMessage());
}
/**
* 拦截Exception类的异常
*
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public ResultData exceptionHandler(Exception e) {
log.error("error-info", e);
return ResultData.error("系统异常");
}
/**
* restTemplate 请求异常
*
* @param e
* @return
* @Title: exceptionHandler
* @Description:
* @author gsh
* @date 2019年10月15日
*/
@ExceptionHandler(RestClientException.class)
@ResponseBody
public ResultData exceptionHandler(RestClientException e) {
log.error("error-info", e);
return ResultData.error(ResultData.REQUEST_INTERFACE_ERROE_CODE, "请求接口异常:" + e.getMessage());
}
/**
* @param e
* @return
* @return Map
* @Title: exceptionHandler
* @Description: 自定义异常处理
* @author:gsh
* @date: 2018年11月30日
*/
@ExceptionHandler(BusinessException.class)
@ResponseBody
public ResultData exceptionHandler(BusinessException e) {
log.warn("自定义异常:-----------------" + e.getMessage());
return ResultData.error(ResultData.ESTIMATE_ERROR_CODE, e.getMessage());
}
/**
* 用户登陆过期
*
* @param e
* @return
* @Title: exceptionHandler
* @Description:
* @author gsh
* @date 2019年10月11日
*/
@ExceptionHandler(LoginExpiredException.class)
@ResponseBody
public ResultData exceptionHandler(LoginExpiredException e) {
JSONObject data = new JSONObject();
String host = SaHolder.getRequest().getUrl().replaceAll(SaHolder.getRequest().getRequestPath(), "");
String clientLoginUrl = host + "/sso/login?back=" + SaFoxUtil.encodeUrl(SaHolder.getRequest().getHeader("client-url"));
data.put("clientLoginUrl", clientLoginUrl);
//SaHolder.getRequest().getHeader("client-url")
return ResultData.error(ResultData.USER_LOGIN_EXPIRED, e.getMessage(), data);
}
/**
* 用户未过期
*
* @param e
* @return
* @Title: exceptionHandler
* @Description:
* @author gsh
* @date 2019年10月11日
*/
@ExceptionHandler(LoginOutException.class)
@ResponseBody
public ResultData exceptionHandler(LoginOutException e) {
return ResultData.error(ResultData.USER_NOT_LOGIN, e.getMessage());
}
/**
* 单用户登录异常
*
* @param e
* @return
* @Title: exceptionHandler
* @Description:
* @author gsh
* @date 2019年10月11日
*/
@ExceptionHandler(SingleLoginException.class)
@ResponseBody
public ResultData exceptionHandler(SingleLoginException e) {
return ResultData.error(ResultData.SINGLE_LOGIN_ERROR, e.getMessage());
}
}
LoginInterceptor.java:
package com.todod.ltcloud.manage.interceptor;
import java.lang.reflect.Method;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.dev33.satoken.stp.StpUtil;
import com.todod.ltcloud.manage.annotaion.NotLogging;
import com.todod.ltcloud.manage.exception.LoginExpiredException;
import com.todod.ltcloud.manage.exception.LoginOutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class LoginInterceptor implements HandlerInterceptor {
Logger log = LoggerFactory.getLogger(LoginInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
Method m = hm.getMethod();
if (m.isAnnotationPresent(NotLogging.class)) { // 不需登陆
return true;
}
// 需要登陆
// String token = request.getHeader("X-Token"); // 取得token
//
// if (StringUtils.isBlank(token)) {
// throw new LoginOutException("未登录");
// }
try {
// 校验token
// String userId = EncryptUtil.decrypt(token);
if (StpUtil.isLogin()) {
request.setAttribute("_userId", StpUtil.getLoginId());// 添加用户id
return true;
} else {
throw new LoginOutException("未登录");
}
} catch (Exception e) {
throw new LoginExpiredException("登陆过期");
}
}
return true;
}
}
SaTokenConfigure.java:
package com.todod.ltcloud.manage.interceptor;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.spring.SpringMVCUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import com.todod.ltcloud.manage.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//加载登录适配器
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns("/sso/*");
}
// @Bean
// public SaServletFilter getSaServletFilter() {
// return new SaServletFilter()
// .addInclude("/**")
// .addExclude("/sso/*", "/favicon.ico")
// .setAuth(obj -> {
//// if(StpUtil.isLogin() == false) {
//// String back = SaFoxUtil.joinParam(SaHolder.getRequest().getUrl(), SpringMVCUtil.getRequest().getQueryString());
//// SaHolder.getResponse().redirect("/sso/login?back=" + SaFoxUtil.encodeUrl(back));
//// SaRouter.back();
//// }
// });
// }
}
SsoClientController.java:
package com.todod.ltcloud.manage.controller;
import cn.dev33.satoken.sso.SaSsoHandle;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Sa-Token-SSO Client端 Controller
*/
@RestController
public class SsoClientController {
/*
* SSO-Client端:处理所有SSO相关请求
*/
@RequestMapping("/sso/*")
public Object ssoRequest() {
return SaSsoHandle.clientRequest();
}
}