标签分类
技术文章
当前位置:主页 > 计算机编程 > java > SpringCloud实现SSO 单点登录的示例代码

SpringCloud实现SSO单点登录的用法及实例

  • 发布时间:
  • 作者:码农之家原创
  • 点击:135

SpringCloud实现SSO 单点登录的示例代码

这篇文章主要知识点是关于SpringCloud,SSO,单点登录,SpringCloud,SSO,SpringCloud,单点登录,SpringCloud实现SSO 单点登录的示例代码,的内容,如果大家想对相关知识点有系统深入的学习,可以参阅以下电子书

Spring Cloud与Docker微服务架构实战
  • 类型:微服务大小:100 MB格式:PDF出版:子工业出版社作者:周立
立即下载

更多相关的学习资源可以参阅 程序设计电子书Java电子书、等栏目。

前言

作为分布式项目,单点登录是必不可少的,文本基于之前的的博客(猛戳:SpringCloud系列——Zuul 动态路由,SpringBoot系列——Redis)记录Zuul配合Redis实现一个简单的sso单点登录实例

sso单点登录思路:

1、访问分布式系统的任意请求,被Zuul的Filter拦截过滤

2、在run方法里实现过滤规则:cookie有令牌accessToken且作为key存在于Redis,或者访问的是登录页面、登录请求则放行

3、否则,将重定向到sso-server的登录页面且原先的请求路径作为一个参数;response.sendRedirect("http://localhost:10010/sso-server/sso/loginPage?url=" + url);

4、登录成功,sso-server生成accessToken,并作为key(用户名+时间戳,这里只是demo,正常项目的令牌应该要更为复杂)存到Redis,value值存用户id作为value(或者直接存储可暴露的部分用户信息也行)设置过期时间(我这里设置3分钟);设置cookie:new Cookie("accessToken",accessToken);,设置maxAge(60*3);、path("/");

5、sso-server单点登录服务负责校验用户信息、获取用户信息、操作Redis缓存,提供接口,在eureka上注册

代码编写

sso-server

首先我们创建一个单点登录服务sso-server,并在eureka上注册(创建项目请参考之前的SpringCloud系列博客跟SpringBoot系列——Redis)

SpringCloud实现SSO 单点登录的示例代码

login.html

我们这里需要用到页面,要先maven引入thymeleaf

 <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-thymeleaf</artifactId>
  </dependency>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
 <meta charset="UTF-8">
 <title>登录页面</title>
</head>
<body>
 <form action="/sso-server/sso/login" method="post">
  <input name="url" type="hidden" th:value="${url}"/>
  用户名:<input name="username" type="text"/>
  密码:<input name="password" type="password"/>
  <input value="登录" type="submit"/>
 </form>
</body>
</html>

提供如下接口

@RestController
@EnableEurekaClient
@SpringBootApplication
public class SsoServerApplication {

 public static void main(String[] args) {
  SpringApplication.run(SsoServerApplication.class, args);
 }

 @Autowired
 private StringRedisTemplate template;

 /**
  * 判断key是否存在
  */
 @RequestMapping("/redis/hasKey/{key}")
 public Boolean hasKey(@PathVariable("key") String key) {
  try {
   return template.hasKey(key);
  } catch (Exception e) {
   e.printStackTrace();
   return false;
  }
 }

 /**
  * 校验用户名密码,成功则返回通行令牌(这里写死huanzi/123456)
  */
 @RequestMapping("/sso/checkUsernameAndPassword")
 private String checkUsernameAndPassword(String username, String password) {
  //通行令牌
  String flag = null;
  if ("huanzi".equals(username) && "123456".equals(password)) {
   //用户名+时间戳(这里只是demo,正常项目的令牌应该要更为复杂)
   flag = username + System.currentTimeMillis();
   //令牌作为key,存用户id作为value(或者直接存储可暴露的部分用户信息也行)设置过期时间(我这里设置3分钟)
   template.opsForValue().set(flag, "1", (long) (3 * 60), TimeUnit.SECONDS);
  }
  return flag;
 }

 /**
  * 跳转登录页面
  */
 @RequestMapping("/sso/loginPage")
 private ModelAndView loginPage(String url) {
  ModelAndView modelAndView = new ModelAndView("login");
  modelAndView.addObject("url", url);
  return modelAndView;
 }

 /**
  * 页面登录
  */
 @RequestMapping("/sso/login")
 private String login(HttpServletResponse response, String username, String password, String url) {
  String check = checkUsernameAndPassword(username, password);
  if (!StringUtils.isEmpty(check)) {
   try {
    Cookie cookie = new Cookie("accessToken", check);
    cookie.setMaxAge(60 * 3);
    //设置域
//    cookie.setDomain("huanzi.cn");
    //设置访问路径
    cookie.setPath("/");
    response.addCookie(cookie);
    //重定向到原先访问的页面
    response.sendRedirect(url);
   } catch (IOException e) {
    e.printStackTrace();
   }
   return null;
  }
  return "登录失败";
 }
}

zuul-server

引入feign,用于调用sso-server服务

 <!-- feign -->
  <dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-openfeign</artifactId>
  </dependency>

创建SsoFeign.java接口

@FeignClient(name = "sso-server", path = "/")
public interface SsoFeign {
 /**
  * 判断key是否存在
  */
 @RequestMapping("redis/hasKey/{key}")
 public Boolean hasKey(@PathVariable("key") String key);

}

启动类加入@EnableFeignClients注解,否则启动会报错,无法注入SsoFeign对象

@EnableZuulProxy
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class ZuulServerApplication {

 public static void main(String[] args) {
  SpringApplication.run(ZuulServerApplication.class, args);
 }

 @Bean
 public AccessFilter accessFilter() {
  return new AccessFilter();
 }
}

修改AccessFilter过滤逻辑,注入feign接口,用于调用sso-server检查Redis,修改run方法的过滤逻辑

/**
 * Zuul过滤器,实现了路由检查
 */
public class AccessFilter extends ZuulFilter {

 @Autowired
 private SsoFeign ssoFeign;

 /**
  * 通过int值来定义过滤器的执行顺序
  */
 @Override
 public int filterOrder() {
  // PreDecoration之前运行
  return PRE_DECORATION_FILTER_ORDER - 1;
 }

 /**
  * 过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型:
  * public static final String ERROR_TYPE = "error";
  * public static final String POST_TYPE = "post";
  * public static final String PRE_TYPE = "pre";
  * public static final String ROUTE_TYPE = "route";
  */
 @Override
 public String filterType() {
  return PRE_TYPE;
 }

 /**
  * 过滤器的具体逻辑
  */
 @Override
 public Object run() {
  RequestContext ctx = RequestContext.getCurrentContext();
  HttpServletRequest request = ctx.getRequest();
  HttpServletResponse response = ctx.getResponse();

  //访问路径
  String url = request.getRequestURL().toString();

  //从cookie里面取值(Zuul丢失Cookie的解决方案:https://blog.csdn.net/lindan1984/article/details/79308396)
  String accessToken = request.getParameter("accessToken");
  for (Cookie cookie : request.getCookies()) {
   if ("accessToken".equals(cookie.getName())) {
    accessToken = cookie.getValue();
   }
  }
  //过滤规则:cookie有令牌且存在于Redis,或者访问的是登录页面、登录请求则放行
  if (url.contains("sso-server/sso/loginPage") || url.contains("sso-server/sso/login") || (!StringUtils.isEmpty(accessToken) && ssoFeign.hasKey(accessToken))) {
   ctx.setSendZuulResponse(true);
   ctx.setResponseStatusCode(200);
   return null;
  } else {
   ctx.setSendZuulResponse(false);
   ctx.setResponseStatusCode(401);
   //重定向到登录页面
   try {
    response.sendRedirect("http://localhost:10010/sso-server/sso/loginPage?url=" + url);
   } catch (IOException e) {
    e.printStackTrace();
   }
   return null;
  }
 }

 /**
  * 返回一个boolean类型来判断该过滤器是否要执行
  */
 @Override
 public boolean shouldFilter() {
  return true;
 }
}

修改配置文件,映射sso-server代理路径,超时时间与丢失cookie的解决

zuul.routes.sso-server.path=/sso-server/**
zuul.routes.sso-server.service-id=sso-server


zuul.host.socket-timeout-millis=60000
zuul.host.connect-timeout-millis=10000
#Zuul丢失Cookie的解决方案:https://blog.csdn.net/lindan1984/article/details/79308396
zuul.sensitive-headers=

测试效果

启动eureka、zuul-server、sso-server、config-server、myspringboot、springdatajpa(由两个应用组成,实现了ribbon负载均衡),记得启动我们的RabbitMQ服务和Redis服务!

SpringCloud实现SSO 单点登录的示例代码

刚开始,没有cookie且无Redis的情况下,浏览器访问http://localhost:10010/myspringboot/feign/ribbon,被zuul-server拦截重定向到sso-server登录页面

SpringCloud实现SSO 单点登录的示例代码

SpringCloud实现SSO 单点登录的示例代码

开始登录校验,为了方便演示,我将密码的type改成text

登录失败,返回提示语

SpringCloud实现SSO 单点登录的示例代码

登录成功,重定向到之前的请求

SpringCloud实现SSO 单点登录的示例代码

cookie的值,以及过期时间

SpringCloud实现SSO 单点登录的示例代码

3分钟后我们再次访问http://localhost:10010/myspringboot/feign/ribbon,cookie、Redis失效,需要从新登录

SpringCloud实现SSO 单点登录的示例代码

SpringCloud实现SSO 单点登录的示例代码

SpringCloud实现SSO 单点登录的示例代码

后记

sso单点登录就记录到这里,这里只是实现了单机版的sso,以后在进行升级吧。

问题报错:我们在sso-server设置cookie后,在zuul-server的run方法里获取不到设置的cookie,去浏览器查看,cookie没有设置成功,Zuul丢失Cookie

解决方案

我们是使用spring cloud zuul作为api-gateway实践中,发现默认zuul会过滤掉cookie等header信息,有些业务场景需要传递这些信息该怎么处理呢?

处理方式   在api-gateway的application.properties文件中添加 zuul.sensitive-headers=  

问题原因

负责根据ServiceId来路由的RibbonRoutingFilter在route之前会调用ProxyRequestHelper的buildZuulRequestHeaders(request)来重新组装一个新的Header。

在buildZuulRequestHeaders方法中会对RequsetHeader中的每一项调用isIncludedHeader(name)来判断当前项是否应该留在新的Header中,如下图,如果当前项在IGNORED_HEADERS(需要忽略的信息)中,就不会在新header中保留。

PreDecorationFilter过滤器会调用ProxyRequestHelper的addIgnoredHeaders方法把敏感信息(ZuulProperties的sensitiveHeaders属性)添加到请求上下文的IGNORED_HEADERS中

sensitiveHeaders的默认值初始值是"Cookie", "Set-Cookie", "Authorization"这三项,可以看到Cookie被列为了敏感信息,所以不会放到新header中

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持码农之家。

以上就是本次给大家分享的全部知识点内容总结,大家还可以在下方相关文章里找到解决axios.interceptors.respon、 儿童python编程入门书籍推、 vue项目中使用md5加密以及、 等java文章进一步学习,感谢大家的阅读和支持。

上一篇:Java实现的矩阵乘法实例代码

下一篇:SpringBoot实现前端验证码图片生成和校验的实例讲解

展开 +

收起 -

学习笔记
网友NO.924853

SpringCloud Zuul实现动态路由

前言 Zuul 是在Spring Cloud Netflix平台上提供动态路由,监控,弹性,安全等边缘服务的框架,是Netflix基于jvm的路由器和服务器端负载均衡器,相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。本文基于上篇(SpringCloud系列——Ribbon 负载均衡)实现Zuul动态路由 GitHub地址:https://github.com/Netflix/zuul 官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.1.0.RC2/single/spring-cloud-netflix.html 代码编写 首先我们在springCloud下面新建一个springboot项目:zuul-server,pom继承parent,并且在Eureka上面注册(还不会服务注册与发现的,请戳:SpringCloud系列——Eureka 服务注册与发现) maven引入Zuul !-- Zuul -- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-netflix-zuul/artifactId /dependency 配置文件 server.port=10010spring.application.name=zuul-servereureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/#健康检查(需要spring-boot-starter-actuator依赖)eureka.client.healthcheck.enabled=true# 续约更新时间间隔(默认30秒)eureka.instance.lease-renewal-interval-in-seconds=10# 续约到期时间(默认90秒)eureka.instance.lease-expiration-duration-in-seconds=10#zuul代理配置 zuul.routes.服务名.path,服务名要与注册的一致#应用名映射zuul.routes.myspringboot.path=/myspringboot/**zuul.routes.myspringboot.service-id=myspring……

网友NO.521847

详解SpringCloud Ribbon 负载均衡通过服务器名无法连接的神坑

一,问题 采取eureka集群、客户端通过Ribbon调用服务,Ribbon端报下列异常 java.net.UnknownHostException: SERVICE-HIjava.lang.IllegalStateException: No instances available for SERVICE-HIjava.lang.IllegalStateException: Request URI does not contain a valid hostname: http://SERVICE-HIcom.netfix.discovery.shared.taransport.TransportException: Cannot execute request on any known server Spring Cloud版本比较乱,版本关联引用更是乱,最终我切换到spring-cloud.version Greenwich.SR1 /spring-cloud.version 异常为: No instances available for SERVICE-HI 二、寻找答案 网上答案千奇百怪 1,Spring Cloud 官网,RestTemplate bean配置中添加负载均衡注解@LoadBalanced,我添加了 @Bean@LoadBalancedpublic RestTemplate getRestTemplate(){ return new RestTemplate();} 结果无效仍然一样报错 2,访问的服务名名称不能有下划线: 我的名称是“SERVICE-HI”本身就不存在下划线,所以不考虑这条。 3,主机名称没在系统文件hosts中配置,ping不通你服务名: 很扯的答案,为什么要配host,负载多台机器让主机名指向谁?不考虑此答案 三,分析问题 百度不到,自己分析原因,发现ribbon服务器没有注册到eureka server中 分析原理:我的客户端服务“SERVICE-HI”已经成功注册到eureka server中了,如果ribbon服务器不在eureka server中注册,是不会知道客户端服务“SERVICE-HI”的存在以及它存在的位置……

网友NO.519348

详解SpringCloud微服务架构之Hystrix断路器

一:什么是Hystrix 在分布式环境中,许多服务依赖项中的一些将不可避免地失败。Hystrix是一个库,通过添加延迟容差和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点,停止其间的级联故障以及提供回退选项,从而提高系统的整体弹性。 Hystrix旨在执行以下操作 1:对通过第三方客户端库访问(通常通过网络)的依赖关系提供保护并控制延迟和故障。 2:隔离复杂分布式系统中的级联故障。 3:快速发现故障,尽快恢复。 4:回退,尽可能优雅地降级。 5:启用近实时监控,警报和操作控制。 二:为什么需要Hystrix? 大型分布式系统中,一个客户端或者服务依赖外部服务,如果一个服务宕了,那么由于我们设置了服务调用系统超时时间,势必会影响相应时间,在高并发的情况下大多数服务器的线程池就出现阻塞(BLOCK),影响整个线上服务的稳定性。 (图片官方图片) 当一切都健康时,请求可以看起来像这样 当许多后端服务系统中的一个宕掉时,整个用户请求: 如果多个客户端调用同一个异常服务的时候,出现的情况是: 三:Hystrix解决什么问题? 分布式架构中的应用程序具有几十个依赖关系,每个依赖关系在某个时刻将不可避免的出现异常。如果应用程序不与这些外部故障隔离,则可能出现线……

网友NO.115600

SpringCloud之分布式配置中心Spring Cloud Config高可用配置实例代码

一、简介 当要将配置中心部署到生产环境中时,与服务注册中心一样,我们也希望它是一个高可用的应用。Spring Cloud Config实现服务端的高可用非常简单,主要有以下两种方式。 传统模式:不需要为这些服务端做任何额外的配置,只需要遵守一个配置规则,将所有的Config Server都指向同一个Git仓库,这样所有的配置内容就通过统一的共享文件系统来维护。而客户端在指定Config Server位置时,只需要配置Config Server上层的负载均衡设备地址即可, 就如下图所示的结构。 服务模式:除了上面这种传统的实现模式之外,我们也可以将Config Server作为一个普通的微服务应用,纳入Eureka的服务治理体系中。这样我们的微服务应用就可以通过配置中心的服务名来获取配置信息,这种方式比起传统的实现模式来说更加有利于维护,因为对于服务端的负载均衡配置和客户端的配置中心指定都通过服务治理机制一并解决了,既实现了高可用,也实现了自维护。由于这部分的实现需要客户端的配合,具体示例读者可详细阅读 “客户端详解 ”一节中的 “服务化配置中心” 小节。 二、前期准备 一个服务注册中心,EUREKASERVER,端口为5555; 三、改造Config-Server (1)pom.xml,添加spring-cloud-starter-eureka依赖 dependencies dependency groupIdorg.springframework.cloud/group……

<
1
>

Copyright 2018-2019 xz577.com 码农之家

版权责任说明