一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - SpringCloud實現SSO 單點登錄的示例代碼

SpringCloud實現SSO 單點登錄的示例代碼

2021-07-01 14:51huanzi-qch Java教程

作為分布式項目,單點登錄是必不可少的,這篇文章主要介紹了SpringCloud實現SSO 單點登錄的示例代碼,非常具有實用價值,需要的朋友可以參考下

前言

作為分布式項目,單點登錄是必不可少的,文本基于之前的的博客(猛戳: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

?
1
2
3
4
<dependency>
  <groupid>org.springframework.boot</groupid>
  <artifactid>spring-boot-starter-thymeleaf</artifactid>
 </dependency>
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!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>

提供如下接口

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
@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服務

?
1
2
3
4
5
<!-- feign -->
 <dependency>
  <groupid>org.springframework.cloud</groupid>
  <artifactid>spring-cloud-starter-openfeign</artifactid>
 </dependency>

創建ssofeign.java接口

?
1
2
3
4
5
6
7
8
9
@feignclient(name = "sso-server", path = "/")
public interface ssofeign {
 /**
  * 判斷key是否存在
  */
 @requestmapping("redis/haskey/{key}")
 public boolean haskey(@pathvariable("key") string key);
 
}

啟動類加入@enablefeignclients注解,否則啟動會報錯,無法注入ssofeign對象

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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方法的過濾邏輯

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/**
 * 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的解決

?
1
2
3
4
5
6
7
8
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中

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://www.cnblogs.com/huanzi-qch/p/10249227.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 污黄漫 | 亚洲国产欧美日韩在线一区 | 丰腴尤物贵妇浪荡小说 | 热久久免费视频 | 日韩在线观看网站 | 男男同gayxxx | 欧美大屁屁 | 范冰冰好紧好滑好湿 | 精品一区二区三区高清免费不卡 | 大象视频污 | 五月天精品视频在线观看 | 貂蝉沦为姓奴小说 | 亚洲精品一区二区三区中文字幕 | 精品美女国产互换人妻 | 色淫阁小说 | 久见久热 这里只有精品 | 日韩毛片在线视频 | 999热在线精品观看全部 | 99只有精品 | 久9青青cao精品视频在线 | 国产99re在线观看69热 | 日韩在线观看网站 | 万域之王动漫在线观看全集免费播放 | 成人观看免费大片在线观看 | 1769亚洲资源站365在线 | 娇妻与公陈峰姚瑶小说在线阅读 | 99精品国产高清一区二区三区香蕉 | 久久中文字幕亚洲 | 美女扒开肌肌让男人桶 | 欧美成人午夜片一一在线观看 | 天天做日日做天天添天天欢公交车 | 亚洲国产高清一区二区三区 | 欧美综合亚洲图片综合区 | 男同激情视频 | 黄色cc| 日韩成本大片35分钟免费播放 | 日本xxxxxx片免费播放18 | 日本特级a禁片在线播放 | 亚洲高清无在码在线电影 | 99国产情在线视频 | 国产成人在线影院 |