SSO单点登录
1. 单独的登陆系统
有时候做微服务拆分的时候,登录注册会抽取出来单独做一个模块
2. 单点登录的架构
2.0 总体流程
- SSO服务有登陆注册功能,还有验证token的功能
- 除SSO服务外的服务都注册一个拦截器,拦截本服务的请求,如果检测到 URL 或者 Cookie 里有 token(说明SSO登录完了或者被伪造了token),就向SSO发送一个验证token的请求,验证成功就放行;验证失败就 重定向到SSO的登录页面(重定向的时候在URL后拼接本次被拦截的url-A)
// 例子
www.sso.com?service=www.c21w.cc.com
- SSO登录成功后,往redis中存放用户信息,然后给客户端颁发 token ,并按照url-A重定向到url-A请求
- 之后再次被拦截,去验证token
2.1 创建一个sso模块
2.1.1 创建登录注册页面以及对应的请求路径
请求接收的参数要包含url-A
请求中还要验证token的合法性,如果通过,就跳转url-A,如果没有通过,就跳转到SSO登陆页面
2.1.2 创建POST表单提交请求
请求接收的参数要包含url-A
请求中判断登陆成功就把 用户信息存放redis, 并且向客户端颁发token, 然后跳转url-A,否则跳转SSO登陆页面
流程图
流程图 2
java代码流程:
- 如果url中有token参数,就去SSO验证token是否合法,合法就保存到cookie中,然后把token存入响应头之后放行
- 如果cookie中有token参数,就去SSO验证token是否合法,合法就把token存入响应头之后放行。
- 都不满足但是是不用允许通行的url,就放行
- 都不满足就重定向到SSO去登录~~
@Order(-1)
@Component
public class AuthFilter implements GlobalFilter {
@Value("${let.path}")
private List<String> LET_PATH;
@Autowired
private UserFeignClient feignClient;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取token
String cookieToken = Optional.ofNullable(exchange.getRequest().getCookies().get("token")).orElse(new ArrayList<>()).stream().map(s -> s.toString().split("=")[1]).findFirst().orElse(null);
String paramToken = Optional.ofNullable(Optional.ofNullable(exchange.getRequest().getQueryParams().get("token")).orElse(new ArrayList<>()).stream().findFirst().orElse(null)).orElse(cookieToken);
// 如果 paramToken 存在
if(paramToken != null){
boolean authToken = feignClient.authToken(paramToken); //验证token
if(authToken){ //如果通过 创建cookie并把token放入响应头
ResponseCookie cookie = ResponseCookie.from("token",paramToken)
.maxAge(60*60)
.httpOnly(true)
.path("/")
.build();
exchange.getResponse().addCookie(cookie);
exchange.getResponse().getHeaders().set("token",paramToken);
return chain.filter(exchange); // 放行
}
}
// 如果 cookieToken 存在
if(cookieToken != null){
boolean authToken = feignClient.authToken(cookieToken); //验证token
if(authToken){ //如果通过 把token放入响应头
exchange.getResponse().getHeaders().set("token",cookieToken);
return chain.filter(exchange); // 放行
}
}
// 如果是不用拦截的网址就放行
long count = Optional.ofNullable(LET_PATH).orElse(new ArrayList<>()).stream().filter(s -> exchange.getRequest().getPath().toString().startsWith(s + "/")).count();
if(count > 0)
return chain.filter(exchange);
// 重定向到SSO登录页面
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.SEE_OTHER);
response.getHeaders().set("Location", "SSO服务地址");
return exchange.getResponse().setComplete();
}
}