springcloud微服務(wù)基于redis集群的單點(diǎn)登錄實(shí)現(xiàn)解析
簡介
本文介紹微服務(wù)架構(gòu)中如何實(shí)現(xiàn)單點(diǎn)登錄功能
創(chuàng)建三個服務(wù):
- 操作redis集群的服務(wù),用于多個服務(wù)之間共享數(shù)據(jù)
- 統(tǒng)一認(rèn)證中心服務(wù),用于整個系統(tǒng)的統(tǒng)一登錄認(rèn)證
- 服務(wù)消費(fèi)者,用于測試單點(diǎn)登錄
大體思路:每個服務(wù)都設(shè)置一個攔截器檢查cookie中是否有token,若有token,則放行,若沒有token,重定向到統(tǒng)一認(rèn)證中心服務(wù)進(jìn)行登錄,登錄成功后返回到被攔截的服務(wù)。
搭建redis集群服務(wù)
搭建統(tǒng)一認(rèn)證中心
主函數(shù)添加注解
/**
* 單點(diǎn)登錄既要注冊到服務(wù)注冊中心,又要向redis服務(wù)系統(tǒng)獲取鼓舞
* 所以要添加 @EnableDiscoveryClient @EnableEurekaClient 兩個注解
*
*/
@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)部錯誤"));
} 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;
//如果緩存中沒有數(shù)據(jù),從數(shù)據(jù)庫取數(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沒有熔斷,也就是返回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;
}
//熔斷后返回錯誤提示
else {
redirectAttributes.addFlashAttribute("message", "服務(wù)器異常");
}
}
//登錄失敗
else {
redirectAttributes.addFlashAttribute("message", "用戶名或密碼錯誤");
}
return "redirect:/login";
}
登錄跳轉(zhuǎn)
@Autowired
private LoginService loginService;
@Autowired
private RedisService redisService;
/**
* 跳轉(zhuǎn)登錄頁
*/
@GetMapping("login")
public String login(HttpServletRequest request,
Model model,
@RequestParam(required = false) String url
) {
String token = CookieUtil.getCookie(request, "token");
//token不為空可能已登錄,從redis獲取賬號
if (token != null && token.trim().length() != 0) {
String loginCode = redisService.get(token);
//如果賬號不為空,從redis獲取該賬號的個人信息
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;
}
}
//將登錄信息傳到登錄頁
model.addAttribute("user", user);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return "login";
}
搭建服務(wù)消費(fèi)者:添加一個攔截器,判斷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為空,一定沒有登錄
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)建局部會話
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注解自動注入
@Bean
WebAdminInterceptor webAdminInterceptor() {
return new WebAdminInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(webAdminInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/static");
}
}
任意寫一個接口,觸發(fā)攔截器進(jìn)行測試
@RequestMapping(value = {"/login"})
public String index(){
return "index";
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 詳解Redis開啟遠(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管理用戶登錄會話的方法
- 基于Redis實(shí)現(xiàn)每日登錄失敗次數(shù)限制
- php+redis實(shí)現(xiàn)注冊、刪除、編輯、分頁、登錄、關(guān)注等功能示例
- 基于session?Redis實(shí)現(xiàn)登錄
相關(guān)文章
idea新建springboot項(xiàng)目pom文件報(bào)錯問題及解決
這篇文章主要介紹了idea新建springboot項(xiàng)目pom文件報(bào)錯問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04
java 算法之歸并排序詳解及實(shí)現(xiàn)代碼
這篇文章主要介紹了java 算法之歸并排序詳解及實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-03-03
java隨機(jī)驗(yàn)證碼生成實(shí)現(xiàn)實(shí)例代碼
這篇文章主要介紹了java隨機(jī)驗(yàn)證碼生成實(shí)現(xiàn)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-05-05
Java基于Calendar類輸出指定年份和月份的日歷代碼實(shí)例
這篇文章主要介紹了Java 使用Calendar類輸出指定年份和月份的日歷,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
Java基于TCP協(xié)議socket網(wǎng)絡(luò)編程的文件傳送的實(shí)現(xiàn)
這篇文章主要介紹了Java基于TCP協(xié)議socket網(wǎng)絡(luò)編程的文件傳送的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Java在并發(fā)環(huán)境中SimpleDateFormat多種解決方案
這篇文章主要介紹了Java在并發(fā)環(huán)境中SimpleDateFormat多種解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
IntellJ IDEA JAVA代碼任務(wù)標(biāo)記實(shí)例解析
這篇文章主要介紹了IntellJ IDEA JAVA代碼任務(wù)標(biāo)記實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07

