最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringCloud實(shí)現(xiàn)SSO 單點(diǎn)登錄的示例代碼

 更新時(shí)間:2019年01月11日 09:41:11   作者:huanzi-qch  
作為分布式項(xiàng)目,單點(diǎn)登錄是必不可少的,這篇文章主要介紹了SpringCloud實(shí)現(xiàn)SSO 單點(diǎn)登錄的示例代碼,非常具有實(shí)用價(jià)值,需要的朋友可以參考下

前言

作為分布式項(xiàng)目,單點(diǎn)登錄是必不可少的,文本基于之前的的博客(猛戳:SpringCloud系列——Zuul 動(dòng)態(tài)路由,SpringBoot系列——Redis)記錄Zuul配合Redis實(shí)現(xiàn)一個(gè)簡(jiǎn)單的sso單點(diǎn)登錄實(shí)例

sso單點(diǎn)登錄思路:

1、訪問(wèn)分布式系統(tǒng)的任意請(qǐng)求,被Zuul的Filter攔截過(guò)濾

2、在run方法里實(shí)現(xiàn)過(guò)濾規(guī)則:cookie有令牌accessToken且作為key存在于Redis,或者訪問(wèn)的是登錄頁(yè)面、登錄請(qǐng)求則放行

3、否則,將重定向到sso-server的登錄頁(yè)面且原先的請(qǐng)求路徑作為一個(gè)參數(shù);response.sendRedirect("http://localhost:10010/sso-server/sso/loginPage?url=" + url);

4、登錄成功,sso-server生成accessToken,并作為key(用戶名+時(shí)間戳,這里只是demo,正常項(xiàng)目的令牌應(yīng)該要更為復(fù)雜)存到Redis,value值存用戶id作為value(或者直接存儲(chǔ)可暴露的部分用戶信息也行)設(shè)置過(guò)期時(shí)間(我這里設(shè)置3分鐘);設(shè)置cookie:new Cookie("accessToken",accessToken);,設(shè)置maxAge(60*3);、path("/");

5、sso-server單點(diǎn)登錄服務(wù)負(fù)責(zé)校驗(yàn)用戶信息、獲取用戶信息、操作Redis緩存,提供接口,在eureka上注冊(cè)

代碼編寫

sso-server

首先我們創(chuàng)建一個(gè)單點(diǎn)登錄服務(wù)sso-server,并在eureka上注冊(cè)(創(chuàng)建項(xiàng)目請(qǐng)參考之前的SpringCloud系列博客跟SpringBoot系列——Redis

login.html

我們這里需要用到頁(yè)面,要先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>登錄頁(yè)面</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;
  }
 }

 /**
  * 校驗(yàn)用戶名密碼,成功則返回通行令牌(這里寫死huanzi/123456)
  */
 @RequestMapping("/sso/checkUsernameAndPassword")
 private String checkUsernameAndPassword(String username, String password) {
  //通行令牌
  String flag = null;
  if ("huanzi".equals(username) && "123456".equals(password)) {
   //用戶名+時(shí)間戳(這里只是demo,正常項(xiàng)目的令牌應(yīng)該要更為復(fù)雜)
   flag = username + System.currentTimeMillis();
   //令牌作為key,存用戶id作為value(或者直接存儲(chǔ)可暴露的部分用戶信息也行)設(shè)置過(guò)期時(shí)間(我這里設(shè)置3分鐘)
   template.opsForValue().set(flag, "1", (long) (3 * 60), TimeUnit.SECONDS);
  }
  return flag;
 }

 /**
  * 跳轉(zhuǎn)登錄頁(yè)面
  */
 @RequestMapping("/sso/loginPage")
 private ModelAndView loginPage(String url) {
  ModelAndView modelAndView = new ModelAndView("login");
  modelAndView.addObject("url", url);
  return modelAndView;
 }

 /**
  * 頁(yè)面登錄
  */
 @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);
    //設(shè)置域
//    cookie.setDomain("huanzi.cn");
    //設(shè)置訪問(wèn)路徑
    cookie.setPath("/");
    response.addCookie(cookie);
    //重定向到原先訪問(wèn)的頁(yè)面
    response.sendRedirect(url);
   } catch (IOException e) {
    e.printStackTrace();
   }
   return null;
  }
  return "登錄失敗";
 }
}

zuul-server

引入feign,用于調(diào)用sso-server服務(wù)

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

創(chuàng)建SsoFeign.java接口

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

}

啟動(dòng)類加入@EnableFeignClients注解,否則啟動(dòng)會(huì)報(bào)錯(cuò),無(wú)法注入SsoFeign對(duì)象

@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過(guò)濾邏輯,注入feign接口,用于調(diào)用sso-server檢查Redis,修改run方法的過(guò)濾邏輯

/**
 * Zuul過(guò)濾器,實(shí)現(xiàn)了路由檢查
 */
public class AccessFilter extends ZuulFilter {

 @Autowired
 private SsoFeign ssoFeign;

 /**
  * 通過(guò)int值來(lái)定義過(guò)濾器的執(zhí)行順序
  */
 @Override
 public int filterOrder() {
  // PreDecoration之前運(yùn)行
  return PRE_DECORATION_FILTER_ORDER - 1;
 }

 /**
  * 過(guò)濾器的類型,在zuul中定義了四種不同生命周期的過(guò)濾器類型:
  * 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;
 }

 /**
  * 過(guò)濾器的具體邏輯
  */
 @Override
 public Object run() {
  RequestContext ctx = RequestContext.getCurrentContext();
  HttpServletRequest request = ctx.getRequest();
  HttpServletResponse response = ctx.getResponse();

  //訪問(wèn)路徑
  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();
   }
  }
  //過(guò)濾規(guī)則:cookie有令牌且存在于Redis,或者訪問(wèn)的是登錄頁(yè)面、登錄請(qǐng)求則放行
  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);
   //重定向到登錄頁(yè)面
   try {
    response.sendRedirect("http://localhost:10010/sso-server/sso/loginPage?url=" + url);
   } catch (IOException e) {
    e.printStackTrace();
   }
   return null;
  }
 }

 /**
  * 返回一個(gè)boolean類型來(lái)判斷該過(guò)濾器是否要執(zhí)行
  */
 @Override
 public boolean shouldFilter() {
  return true;
 }
}

修改配置文件,映射sso-server代理路徑,超時(shí)時(shí)間與丟失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=

測(cè)試效果

啟動(dòng)eureka、zuul-server、sso-server、config-server、myspringboot、springdatajpa(由兩個(gè)應(yīng)用組成,實(shí)現(xiàn)了ribbon負(fù)載均衡),記得啟動(dòng)我們的RabbitMQ服務(wù)和Redis服務(wù)!

剛開始,沒(méi)有cookie且無(wú)Redis的情況下,瀏覽器訪問(wèn)http://localhost:10010/myspringboot/feign/ribbon,被zuul-server攔截重定向到sso-server登錄頁(yè)面

開始登錄校驗(yàn),為了方便演示,我將密碼的type改成text

登錄失敗,返回提示語(yǔ)

登錄成功,重定向到之前的請(qǐng)求

cookie的值,以及過(guò)期時(shí)間

3分鐘后我們?cè)俅卧L問(wèn)http://localhost:10010/myspringboot/feign/ribbon,cookie、Redis失效,需要從新登錄

后記

sso單點(diǎn)登錄就記錄到這里,這里只是實(shí)現(xiàn)了單機(jī)版的sso,以后在進(jìn)行升級(jí)吧。

問(wèn)題報(bào)錯(cuò):我們?cè)趕so-server設(shè)置cookie后,在zuul-server的run方法里獲取不到設(shè)置的cookie,去瀏覽器查看,cookie沒(méi)有設(shè)置成功,Zuul丟失Cookie

解決方案

我們是使用spring cloud zuul作為api-gateway實(shí)踐中,發(fā)現(xiàn)默認(rèn)zuul會(huì)過(guò)濾掉cookie等header信息,有些業(yè)務(wù)場(chǎng)景需要傳遞這些信息該怎么處理呢?

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

問(wèn)題原因

負(fù)責(zé)根據(jù)ServiceId來(lái)路由的RibbonRoutingFilter在route之前會(huì)調(diào)用ProxyRequestHelper的buildZuulRequestHeaders(request)來(lái)重新組裝一個(gè)新的Header。

在buildZuulRequestHeaders方法中會(huì)對(duì)RequsetHeader中的每一項(xiàng)調(diào)用isIncludedHeader(name)來(lái)判斷當(dāng)前項(xiàng)是否應(yīng)該留在新的Header中,如下圖,如果當(dāng)前項(xiàng)在IGNORED_HEADERS(需要忽略的信息)中,就不會(huì)在新header中保留。

PreDecorationFilter過(guò)濾器會(huì)調(diào)用ProxyRequestHelper的addIgnoredHeaders方法把敏感信息(ZuulProperties的sensitiveHeaders屬性)添加到請(qǐng)求上下文的IGNORED_HEADERS中

sensitiveHeaders的默認(rèn)值初始值是"Cookie", "Set-Cookie", "Authorization"這三項(xiàng),可以看到Cookie被列為了敏感信息,所以不會(huì)放到新header中

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 為什么說(shuō)HashMap線程不安全

    為什么說(shuō)HashMap線程不安全

    本文主要介紹了為什么說(shuō)HashMap線程不安全,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Java集合的定義與Collection類使用詳解

    Java集合的定義與Collection類使用詳解

    這篇文章主要介紹了Java集合的定義及Collection工具類使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-11-11
  • Java?超詳細(xì)帶你掌握矩陣的運(yùn)算

    Java?超詳細(xì)帶你掌握矩陣的運(yùn)算

    在學(xué)習(xí)機(jī)器學(xué)習(xí)算法時(shí),發(fā)現(xiàn)運(yùn)用java?來(lái)實(shí)現(xiàn)有些算法代碼時(shí),會(huì)有很大困難,其中有一點(diǎn)就是?java?本身并沒(méi)有矩陣運(yùn)算的?api,所以進(jìn)行要實(shí)現(xiàn)矩陣運(yùn)算就尤其復(fù)雜,讓我們一起了解矩陣的運(yùn)算
    2022-03-03
  • Spring Boot 初始化運(yùn)行特定方法解析

    Spring Boot 初始化運(yùn)行特定方法解析

    這篇文章主要介紹了Spring Boot 初始化運(yùn)行特定方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • java中Pulsar?InterruptedException?異常

    java中Pulsar?InterruptedException?異常

    這篇文章主要為大家介紹了java中Pulsar?InterruptedException?異常分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • SpringSecurity入門使用教程

    SpringSecurity入門使用教程

    ? Spring Security是一個(gè)功能強(qiáng)大且高度可定制的身份驗(yàn)證和訪問(wèn)控制框架,它是用于保護(hù)基于Spring的應(yīng)用程序的實(shí)際標(biāo)準(zhǔn),這篇文章主要介紹了如何使用SpringSecurity,需要的朋友可以參考下
    2023-12-12
  • 利用spring-boot-maven-plugin插件打包SpringBoot應(yīng)用方式

    利用spring-boot-maven-plugin插件打包SpringBoot應(yīng)用方式

    spring-boot-maven-plugin插件可以將SpringBoot應(yīng)用打成帶依賴的jar包,該包中不僅包含應(yīng)用自身的代碼,還包含了pom.xml中配置的依賴,修改pom.xml打包后,生成的jar包就包含了項(xiàng)目依賴,生成的jar包位于項(xiàng)目的target文件夾下
    2025-02-02
  • spring boot mogodb多條件拼接的解決方法

    spring boot mogodb多條件拼接的解決方法

    這篇文章主要介紹了spring boot mogodb多條件拼接的解決方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-08-08
  • 你必須得會(huì)的SpringBoot全局統(tǒng)一處理異常詳解

    你必須得會(huì)的SpringBoot全局統(tǒng)一處理異常詳解

    程序在運(yùn)行的過(guò)程中,不可避免會(huì)產(chǎn)生各種各樣的錯(cuò)誤,這個(gè)時(shí)候就需要進(jìn)行異常處理,本文主要為大家介紹了SpringBoot實(shí)現(xiàn)全局統(tǒng)一處理異常的方法,需要的可以參考一下
    2023-06-06
  • 詳解Java中CountDownLatch異步轉(zhuǎn)同步工具類

    詳解Java中CountDownLatch異步轉(zhuǎn)同步工具類

    今天給大家?guī)?lái)的是關(guān)于Java的相關(guān)知識(shí),文章圍繞著CountDownLatch異步轉(zhuǎn)同步工具類展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06

最新評(píng)論

萝北县| 禹城市| 宁陕县| 和顺县| 遵化市| 巩义市| 文化| 柘城县| 北京市| 惠来县| 噶尔县| 玉田县| 涞源县| 兴国县| 泌阳县| 贺兰县| 阳原县| 五峰| 元朗区| 天祝| 报价| 吉隆县| 府谷县| 勐海县| 施秉县| 望谟县| 龙岩市| 福建省| 通城县| 河东区| 揭阳市| 乌拉特中旗| 江孜县| 瑞昌市| 玉屏| 郎溪县| 长丰县| 台湾省| 方城县| 内丘县| 砀山县|