springcloud微服務(wù)基于redis集群的單點(diǎn)登錄實(shí)現(xiàn)解析
簡(jiǎn)介
本文介紹微服務(wù)架構(gòu)中如何實(shí)現(xiàn)單點(diǎn)登錄功能
創(chuàng)建三個(gè)服務(wù):
- 操作redis集群的服務(wù),用于多個(gè)服務(wù)之間共享數(shù)據(jù)
- 統(tǒng)一認(rèn)證中心服務(wù),用于整個(gè)系統(tǒng)的統(tǒng)一登錄認(rèn)證
- 服務(wù)消費(fèi)者,用于測(cè)試單點(diǎn)登錄
大體思路:每個(gè)服務(wù)都設(shè)置一個(gè)攔截器檢查cookie中是否有token,若有token,則放行,若沒(méi)有token,重定向到統(tǒng)一認(rèn)證中心服務(wù)進(jìn)行登錄,登錄成功后返回到被攔截的服務(wù)。
搭建redis集群服務(wù)
搭建統(tǒng)一認(rèn)證中心
主函數(shù)添加注解
/** * 單點(diǎn)登錄既要注冊(cè)到服務(wù)注冊(cè)中心,又要向redis服務(wù)系統(tǒng)獲取鼓舞 * 所以要添加 @EnableDiscoveryClient @EnableEurekaClient 兩個(gè)注解 * */ @EnableDiscoveryClient @EnableEurekaClient @EnableFeignClients @MapperScan(basePackages = "com.example.itokenservicesso.mapper") @SpringBootApplication public class ItokenServiceSsoApplication { public static void main(String[] args) { SpringApplication.run(ItokenServiceSsoApplication.class, args); } }
消費(fèi)redis服務(wù)和熔斷器
@FeignClient(value = "itoken-service-redis", fallback = RedisServiceFallBack.class) public interface RedisService { @PostMapping(value = "put") public String put(@RequestParam(value = "key") String key, @RequestParam(value = "value") String value, @RequestParam(value = "seconds") long seconds); @GetMapping(value = "get") public String get(@RequestParam(value = "key") String key); }
@Component public class RedisServiceFallBack implements RedisService { @Override public String put(String key, String value, long seconds) { return FallBack.badGateWay(); } @Override public String get(String key) { return FallBack.badGateWay(); } }
public class FallBack { public static String badGateWay(){ try { return JsonUtil.objectToString(ResultUtil.error(502,"內(nèi)部錯(cuò)誤")); } catch (JsonProcessingException e) { e.printStackTrace(); } return null; } }
登錄服務(wù)
@Service public class LoginServiceImpl implements LoginService { @Autowired private UserMapper userMapper; @Autowired private RedisService redisService; @Override public User login(String loginCode, String plantPassword) { //從緩存中獲取登錄用戶的數(shù)據(jù) String json = redisService.get(loginCode); User user = null; //如果緩存中沒(méi)有數(shù)據(jù),從數(shù)據(jù)庫(kù)取數(shù)據(jù) if (json == null) { user = userMapper.selectAll(loginCode); String passwordMd5 = DigestUtils.md5DigestAsHex(plantPassword.getBytes()); if (user != null && passwordMd5.equals(user.getPassword())) { //登錄成功,刷新緩存 try { redisService.put(loginCode, JsonUtil.objectToString(user), 60 * 60 * 24); } catch (JsonProcessingException e) { e.printStackTrace(); } return user; } else { return null; } } //如果緩存中有數(shù)據(jù) else { try { user = JsonUtil.stringToObject(json, User.class); } catch (IOException e) { e.printStackTrace(); } } return user; } }
contoller層,處理登錄業(yè)務(wù)和登錄跳轉(zhuǎn)
登錄業(yè)務(wù)
/** * 登錄業(yè)務(wù) * * @param loginCode * @param password * @return */ @PostMapping("login") public String login(String loginCode, String password, @RequestParam(required = false) String url, HttpServletRequest request, HttpServletResponse response, RedirectAttributes redirectAttributes) { User user = loginService.login(loginCode, password); //登錄成功 if (user != null) { String token = UUID.randomUUID().toString(); //將token放入緩存 String result = redisService.put(token, loginCode, 60 * 60 * 24); //如果redisService沒(méi)有熔斷,也就是返回ok,才能執(zhí)行 if (result != null && result.equals("ok")) { CookieUtil.setCookie(response, "token", token, 60 * 60 * 24); if (url != null && !url.trim().equals("")) return "redirect:" + url; } //熔斷后返回錯(cuò)誤提示 else { redirectAttributes.addFlashAttribute("message", "服務(wù)器異常"); } } //登錄失敗 else { redirectAttributes.addFlashAttribute("message", "用戶名或密碼錯(cuò)誤"); } return "redirect:/login"; }
登錄跳轉(zhuǎn)
@Autowired private LoginService loginService; @Autowired private RedisService redisService; /** * 跳轉(zhuǎn)登錄頁(yè) */ @GetMapping("login") public String login(HttpServletRequest request, Model model, @RequestParam(required = false) String url ) { String token = CookieUtil.getCookie(request, "token"); //token不為空可能已登錄,從redis獲取賬號(hào) if (token != null && token.trim().length() != 0) { String loginCode = redisService.get(token); //如果賬號(hào)不為空,從redis獲取該賬號(hào)的個(gè)人信息 if (loginCode != null && loginCode.trim().length() != 0) { String json = redisService.get(loginCode); if (json != null && json.trim().length() != 0) { try { User user = JsonUtil.stringToObject(json, User.class); //已登錄 if (user != null) { if (url != null && url.trim().length() != 0) { return "redirect:" + url; } } //將登錄信息傳到登錄頁(yè) model.addAttribute("user", user); } catch (IOException e) { e.printStackTrace(); } } } } return "login"; }
搭建服務(wù)消費(fèi)者:添加一個(gè)攔截器,判斷token是否為空
攔截器
public class WebAdminInterceptor implements HandlerInterceptor { @Autowired private RedisService redisService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = CookieUtil.getCookie(request, "token"); //token為空,一定沒(méi)有登錄 if (token == null || token.isEmpty()) { response.sendRedirect("http://localhost:8503/login?url=http://localhost:8601/login"); return false; } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HttpSession session = request.getSession(); User user = (User) session.getAttribute("user"); //已登陸狀態(tài) if (user != null) { if (modelAndView != null) { modelAndView.addObject("user", user); } } //未登錄狀態(tài) else { String token = CookieUtil.getCookie(request, "token"); if (token != null && !token.isEmpty()) { String loginCode = redisService.get(token); if (loginCode != null && !loginCode.isEmpty()) { String json = redisService.get(loginCode); if (json != null && !json.isEmpty()) { //已登錄狀態(tài),創(chuàng)建局部會(huì)話 user = JsonUtil.stringToObject(json, User.class); if (modelAndView != null) { modelAndView.addObject("user", user); } request.getSession().setAttribute("user", user); } } } } //二次確認(rèn)是否有用戶信息 if (user == null) { response.sendRedirect("http://localhost:8503/login?url=http://localhost:8601/login"); } } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
配置攔截器
@Configuration public class WebAdminInterceptorConfig implements WebMvcConfigurer { //將攔截器設(shè)置為Bean,在攔截其中才能使用@AutoWired注解自動(dòng)注入 @Bean WebAdminInterceptor webAdminInterceptor() { return new WebAdminInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(webAdminInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/static"); } }
任意寫(xiě)一個(gè)接口,觸發(fā)攔截器進(jìn)行測(cè)試
@RequestMapping(value = {"/login"}) public String index(){ return "index"; }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 詳解Redis開(kāi)啟遠(yuǎn)程登錄連接
- redis-cli 使用密碼登錄的實(shí)例
- 實(shí)例詳解Spring Boot實(shí)戰(zhàn)之Redis緩存登錄驗(yàn)證碼
- 詳解Redis使用認(rèn)證密碼登錄
- 基于springboot和redis實(shí)現(xiàn)單點(diǎn)登錄
- SpringBoot+Vue+Redis實(shí)現(xiàn)單點(diǎn)登錄(一處登錄另一處退出登錄)
- 使用redis管理用戶登錄會(huì)話的方法
- 基于Redis實(shí)現(xiàn)每日登錄失敗次數(shù)限制
- php+redis實(shí)現(xiàn)注冊(cè)、刪除、編輯、分頁(yè)、登錄、關(guān)注等功能示例
- 基于session?Redis實(shí)現(xiàn)登錄
相關(guān)文章
idea新建springboot項(xiàng)目pom文件報(bào)錯(cuò)問(wèn)題及解決
這篇文章主要介紹了idea新建springboot項(xiàng)目pom文件報(bào)錯(cuò)問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04java 算法之歸并排序詳解及實(shí)現(xiàn)代碼
這篇文章主要介紹了java 算法之歸并排序詳解及實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-03-03java隨機(jī)驗(yàn)證碼生成實(shí)現(xiàn)實(shí)例代碼
這篇文章主要介紹了java隨機(jī)驗(yàn)證碼生成實(shí)現(xiàn)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-05-05Java基于Calendar類輸出指定年份和月份的日歷代碼實(shí)例
這篇文章主要介紹了Java 使用Calendar類輸出指定年份和月份的日歷,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02Java基于TCP協(xié)議socket網(wǎng)絡(luò)編程的文件傳送的實(shí)現(xiàn)
這篇文章主要介紹了Java基于TCP協(xié)議socket網(wǎng)絡(luò)編程的文件傳送的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Java在并發(fā)環(huán)境中SimpleDateFormat多種解決方案
這篇文章主要介紹了Java在并發(fā)環(huán)境中SimpleDateFormat多種解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07IntellJ IDEA JAVA代碼任務(wù)標(biāo)記實(shí)例解析
這篇文章主要介紹了IntellJ IDEA JAVA代碼任務(wù)標(biāo)記實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07