SpringBoot集成redis與session實(shí)現(xiàn)分布式單點(diǎn)登錄
前言:
由于考慮到cookie的安全性問題,就有了下面這個(gè)版本的sso
單點(diǎn)登錄 SSO(Single Sign On)
什么是單點(diǎn)登錄?
單點(diǎn)登錄的英文名叫做:Single Sign On(簡稱SSO),指在同一帳號平臺下的多個(gè)應(yīng)用系統(tǒng)中,用戶只需登錄一次,即可訪問所有相互信任的系統(tǒng)。簡而言之,多個(gè)系統(tǒng),統(tǒng)一登陸。
我們可以這樣理解,在一個(gè)服務(wù)模塊登錄后,其他模塊無需再登錄
實(shí)現(xiàn)方式
- session廣播機(jī)制實(shí)現(xiàn)(老方法) ? 當(dāng)模塊較多時(shí),比較浪費(fèi)資源;數(shù)據(jù)冗余,存在多份一樣的數(shù)據(jù)? session默認(rèn)過期時(shí)間30分鐘
- 基于cookie+redis實(shí)現(xiàn)? 在項(xiàng)目中任何一個(gè)模塊登錄后,把數(shù)據(jù)放到兩個(gè)地方? redis:key:生成唯一隨機(jī)值(ip、用戶id等) value:用戶數(shù)據(jù)? cookie:存放redis生成的key值放到cookie? 訪問其他模塊,發(fā)送請求帶著cookie進(jìn)行發(fā)送,服務(wù)器獲取cookie值,在redis中查詢,根據(jù)key進(jìn)行查詢,如果找到就是登錄狀態(tài)
- 分布式session方式實(shí)現(xiàn)單點(diǎn)登錄流程運(yùn)行:(1) 用戶第一次登錄時(shí),將會話信息(用戶Id和用戶信息),比如以用戶Id為Key,寫入分布式Session;(2) 用戶再次登錄時(shí),獲取分布式Session,是否有會話信息,如果沒有則調(diào)到登錄頁;(3) 一般采用Cache中間件實(shí)現(xiàn),建議使用Redis,因此它有持久化功能,方便分布式Session宕機(jī)后,可以從持久化存儲中加載會話信息;(4) 存入會話時(shí),可以設(shè)置會話保持的時(shí)間,比如15分鐘,超過后自動超時(shí);結(jié)合Cache中間件,實(shí)現(xiàn)的分布式Session,可以很好的模擬Session會話。
- token驗(yàn)證在項(xiàng)目某個(gè)模塊進(jìn)行登錄,登錄之后,按照jwt規(guī)則生成字待串,把登錄之后用戶包含到生成字符串里面,把字符串返回
(1)可以把字符串通過cookie返回
(2)把字符串通過地址欄返回前端收到token之后將token存儲在自己的請求頭之中或者url后面,這樣每次請求都可以帶著token請求。再去訪問項(xiàng)目其他模塊,獲取地址欄或者請求頭里面的token,根據(jù)字符串獲取用戶信息。同時(shí)為了設(shè)置失效時(shí)間,可以將token放在redis中,設(shè)置失效時(shí)間,判斷過期。- CAS 中央認(rèn)證服務(wù)
開發(fā)技術(shù)
- SpringBoot
- Redis
- Session
單點(diǎn)登錄實(shí)現(xiàn)流程
- 用戶在登錄時(shí),登錄成功以后得到當(dāng)前sessionid
- 將用戶信息存儲在redis里面,設(shè)置有效時(shí)間30分鐘,以key-value形式,sessionid作為key,登錄成功后的用戶信息作為value
- 訪問時(shí)通過攔截器攔截請求,判斷當(dāng)前sessionid是否在redis里,再則延長壽命,不再提示身份過期
- 完成登錄驗(yàn)證后,放行執(zhí)行訪問請求
實(shí)現(xiàn)案例
實(shí)現(xiàn)效果:使用nginx做輪詢分發(fā)請求,在任何一個(gè)服務(wù)登錄成功以后,在訪問其他服務(wù)時(shí)就不需要再去登錄
- 1,首先創(chuàng)建一個(gè)boot項(xiàng)目
- 2,導(dǎo)入pom依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
3,配置核心文件
注意:這里只有一個(gè)配置文件,若想啟動兩個(gè)端口。就在編輯頁面的 VM OPTIONS 里配置如下 -Dserver.port=8082
server: port: 8081 ## redis #session存儲類型 spring: application: name: redis_cookie redis: host: 127.0.0.1 port: 6379 #沒用就填空 password: jedis: pool: #連接池最大連接數(shù) max-active: 8 #阻塞時(shí)間 (負(fù)表示沒有) max-wait: -1 #最大空閑連接 max-idle: 8 #最小空閑連接 min-idle: 0 #連接超時(shí)時(shí)間 timeout: 30000 database: 0
4,編寫用戶類
package com.gxhh.redis_session.bean; /** * @Program: LoginDemo * @ClassName User * @Description: 用戶類 * @Author: liutao * @Create: 2022/7/8 16:04 * @Version: 1.0 */ public class User { private String username; private String pwd; public User() { } public User(String username, String pwd) { this.username = username; this.pwd = pwd; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public String toString() { return "User{" + "username='" + username + ''' + ", pwd='" + pwd + ''' + '}'; } }
5,編寫登錄接口和業(yè)務(wù)邏輯
package com.gxhh.redis_session.web; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.gxhh.redis_session.bean.User; import com.gxhh.redis_session.utils.CookieUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.UUID; import java.util.concurrent.TimeUnit; /** * @Program: redis_cookie * @ClassName LoginController * @Author: liutao * @Description: 用戶登錄和測試接口 * @Create: 2022-07-09 19:50 * @Version 1.0 **/ @RestController public class LoginController { @Autowired RedisTemplate redisTemplate; @Autowired CookieUtil CookieUtil; @Value("${server.port}") String port; /** * 登錄接口 * @param user User對象 * @return 提示信息 * @throws JsonProcessingException */ @PostMapping(value = "/doLogin", produces = "text/html;charset=utf-8") public String login(HttpServletRequest request, HttpServletResponse response, User user) throws JsonProcessingException { System.out.println(user); ValueOperations ops = redisTemplate.opsForValue(); String s = request.getSession().getId(); if (redisTemplate.hasKey(s)) {//登錄過 return "重復(fù)登錄"; } else {//未登錄 if ("sso".equals(user.getUsername()) && "123456".equals(user.getPwd())) { ObjectMapper om = new ObjectMapper(); ops.set(s, om.writeValueAsString(user));//將憑證存入Redis redisTemplate.expire(s, 30, TimeUnit.MINUTES);//設(shè)置過期時(shí)間,30分鐘 return "登錄成功"; }else { return "登錄失?。?; } } } /** * 退出接口 * @return * @throws JsonProcessingException */ @RequestMapping (value = "/logout", produces = "text/html;charset=utf-8") public String logout(HttpServletRequest request, HttpServletResponse response, User user) throws JsonProcessingException { System.out.println(user); if(redisTemplate.delete(request.getSession().getId())){ request.getSession().invalidate(); return "成功退出,請登錄!"; }else { return "系統(tǒng)異常!"; } } /** * 測試接口 * @param * @return */ @GetMapping("/hello") public String hello(){ return "hello 我是端口"+port; } }
6,配置WebMVC攔截器,攔截所有請求,只放行登錄接口
package com.gxhh.redis_session.config; import com.gxhh.redis_session.interceptor.LoginInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @Program: redis_cookie * @ClassName WebMVCConfig * @Author: liutao * @Description: WebMVC攔截器 * @Create: 2022-07-09 19:50 * @Version 1.0 **/ @Configuration public class WebMVCConfig implements WebMvcConfigurer { @Autowired LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns("/**")//需要攔截的路徑 .excludePathPatterns("/doLogin","/login.html") ;//排除/doLogin路徑 } }
7,配置請求攔截器
package com.gxhh.redis_session.interceptor; import com.fasterxml.jackson.databind.ObjectMapper; import com.gxhh.redis_session.utils.CookieUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; import java.util.concurrent.TimeUnit; /** * @Program: redis_cookie * @ClassName LoginInterceptor * @Author: liutao * @Description: 用戶登錄攔截器,校驗(yàn)session,身份驗(yàn)證 * @Create: 2022-07-09 19:50 * @Version 1.0 **/ @Component public class LoginInterceptor implements HandlerInterceptor { @Autowired RedisTemplate redisTemplate; @Autowired CookieUtil CookieUtil; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("攔截到請求:"+request.getRequestURI()); System.out.println("當(dāng)前令牌:"+request.getSession().getId()); String s = request.getSession().getId(); System.out.println("登錄狀態(tài):"+redisTemplate.hasKey(s)); if (redisTemplate.hasKey(s)) {//延長登錄狀態(tài) redisTemplate.expire(s, 30, TimeUnit.MINUTES);//設(shè)置過期時(shí)間,30分鐘 return true; }else {//身份過期 response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter(); out.write("身份過期,非法請求"); return false; } } }
8,nginx分發(fā)輪詢配置
#user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; upstream mysvr{ server localhost:8081; server localhost:8082; } server { listen 8052; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; # location / { # root html; # index index.html index.htm; # proxy_pass http://localhost:8011; # } location / { # root html; # index index.html index.htm; proxy_pass http://mysvr; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ .php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ .php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /.ht { # deny all; #} }
看效果
先訪問測試接口:
然后再登錄:
訪問測試接口:
關(guān)閉瀏覽器后訪問:
到此這篇關(guān)于SpringBoot集成redis與session實(shí)現(xiàn)分布式單點(diǎn)登錄的文章就介紹到這了,更多相關(guān)SpringBoot集成redis 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
新手小白學(xué)JAVA 日期類Date SimpleDateFormat Calendar(入門)
本文主要介紹了JAVA 日期類Date SimpleDateFormat Calendar,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10java判斷用戶輸入的是否至少含有N位小數(shù)的實(shí)例
下面小編就為大家分享一篇java判斷用戶輸入的是否至少含有N位小數(shù)的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12淺談一下maven優(yōu)缺點(diǎn)及使用和特點(diǎn)
這篇文章主要介紹了淺談一下maven優(yōu)缺點(diǎn)及使用和特點(diǎn),一個(gè)項(xiàng)目管理工具軟件,那么maven項(xiàng)目有什么優(yōu)缺點(diǎn)呢,讓我們一起來看看吧2023-03-03詳解JAVAEE——SSH三大框架整合(spring+struts2+hibernate)
這篇文章主要介紹了詳解JAVAEE——SSH三大框架整合(spring+struts2+hibernate),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Java中兩個(gè)大數(shù)之間的相關(guān)運(yùn)算及BigInteger代碼示例
這篇文章主要介紹了Java中兩個(gè)大數(shù)之間的相關(guān)運(yùn)算及BigInteger代碼示例,通過biginteger類實(shí)現(xiàn)大數(shù)的運(yùn)算代碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11Java實(shí)現(xiàn)簡易圖書借閱系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡易圖書借閱系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03