当前位置: 首页 > 公众号精选 > 架构师社区
[导读]单点登录系统设计思路:采用Spring4 Java配置方式整合HttpClient,Redis ,MySql和SpringBoot的简易教程。

来源: urlify.cn/I3eyAz


单点登录系统设计思路:采用Spring4 Java配置方式整合HttpClient,Redis ,MySql和SpringBoot的简易教程。
基于SpringBoot实现单点登录系统
在传统的系统,或者是只有一个服务器的系统中。Session在一个服务器中,各个模块都可以直接获取,只需登录一次就进入各个模块。若在服务器集群或者是分布式系统架构中,每个服务器之间的Session并不是共享的,这会出现每个模块都要登录的情况。这时候需要通过单点登录系统(Single Sign On)将用户信息存在Redis数据库中实现Session共享的效果。从而实现一次登录就可以访问所有相互信任的应用系统。

一、整合 HttpClient

HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
首先在src/main/resources 目录下创建 httpclient.properties 配置文件。

#设置整个连接池默认最大连接数 http.defaultMaxPerRoute=100 #设置整个连接池最大连接数 http.maxTotal=300 #设置请求超时 http.connectTimeout=1000 #设置从连接池中获取到连接的最长时间 http.connectionRequestTimeout=500 #设置数据传输的最长时间 http.socketTimeout=10000

然后在 src/main/java/com/itdragon/config 目录下创建 HttpclientSpringConfig.java 文件
这里用到了四个很重要的注解
@Configuration : 作用于类上,指明该类就相当于一个xml配置文件
@Bean : 作用于方法上,指明该方法相当于xml配置中的bean,注意方法名的命名规范
@PropertySource : 指定读取的配置文件,引入多个value={“xxx:xxx”,“xxx:xxx”},ignoreResourceNotFound=true 文件不存在时忽略
@Value : 获取配置文件的值

package com.itdragon.config;
/**
 * @Configuration  作用于类上,相当于一个xml配置文件
 * @Bean    作用于方法上,相当于xml配置中的* @PropertySource 指定读取的配置文件,ignoreResourceNotFound=true 文件不存在是忽略
 * @Value   获取配置文件的值
 */
@Configuration
@PropertySource(value = "classpath:httpclient.properties", ignoreResourceNotFound=true)
public class HttpclientSpringConfig {

    @Value("${http.maxTotal}")
    private Integer httpMaxTotal;

    @Value("${http.defaultMaxPerRoute}")
    private Integer httpDefaultMaxPerRoute;

    @Value("${http.connectTimeout}")
    private Integer httpConnectTimeout;

    @Value("${http.connectionRequestTimeout}")
    private Integer httpConnectionRequestTimeout;

    @Value("${http.socketTimeout}")
    private Integer httpSocketTimeout;

    @Autowired
    private PoolingHttpClientConnectionManager manager;

    @Bean
    public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
        // 最大连接数
        poolingHttpClientConnectionManager.setMaxTotal(httpMaxTotal);
        // 每个主机的最大并发数
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(httpDefaultMaxPerRoute); return poolingHttpClientConnectionManager;
    }

    @Bean // 定期清理无效连接
    public IdleConnectionEvictor idleConnectionEvictor() { return new IdleConnectionEvictor(manager, 1L, TimeUnit.HOURS);
    }

    @Bean // 定义HttpClient对象 注意该对象需要设置scope="prototype":多例对象
    @Scope("prototype")
    public CloseableHttpClient closeableHttpClient() { return HttpClients.custom().setConnectionManager(this.manager).build();
    }

    @Bean // 请求配置
    public RequestConfig requestConfig() { return RequestConfig.custom().setConnectTimeout(httpConnectTimeout) // 创建连接的最长时间
                .setConnectionRequestTimeout(httpConnectionRequestTimeout) // 从连接池中获取到连接的最长时间
                .setSocketTimeout(httpSocketTimeout) // 数据传输的最长时间
                .build();
    }

}

二、整合 Redis

SpringBoot官方其实提供了spring-boot-starter-redis pom 帮助我们快速开发,但我们也可以自定义配置,这样可以更方便地掌控。
首先在src/main/resources 目录下创建 redis.properties 配置文件

redis.maxTotal=200
redis.node.host=10.128.15.21
redis.node.port=6379
REDIS_USER_SESSION_KEY=REDIS_USER_SESSION
SSO_SESSION_EXPIRE=30

设置Redis主机的ip地址和端口号,和存入Redis数据库中的key以及存活时间。这里为了方便测试,存活时间设置的比较小。这里的配置是单例Redis。
在src/main/java/com/itdragon/config 目录下创建 RedisSpringConfig.java 文件。

@Configuration
@PropertySource(value = "classpath:redis.properties")
public class RedisSpringConfig {

    @Value("${redis.maxTotal}")
    private Integer redisMaxTotal;

    @Value("${redis.node.host}")
    private String redisNodeHost;

    @Value("${redis.node.port}")
    private Integer redisNodePort;

    private JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(redisMaxTotal); return jedisPoolConfig;
    }
    
    @Bean 
    public JedisPool getJedisPool(){ // 省略第一个参数则是采用 Protocol.DEFAULT_DATABASE
     JedisPool jedisPool = new JedisPool(jedisPoolConfig(), redisNodeHost, redisNodePort); return jedisPool;
    }

    @Bean
    public ShardedJedisPool shardedJedisPool() {
        ListjedisShardInfos = new ArrayList();
        jedisShardInfos.add(new JedisShardInfo(redisNodeHost, redisNodePort)); return new ShardedJedisPool(jedisPoolConfig(), jedisShardInfos);
    }
}

三、Service 层

在src/main/java/com/itdragon/service 目录下创建 UserService.java 文件,它负责三件事情
第一件事情:验证用户信息是否正确,并将登录成功的用户信息保存到Redis数据库中。
第二件事情:负责判断用户令牌是否过期,若没有则刷新令牌存活时间。
第三件事情:负责从Redis数据库中删除用户信息。

package com.itdragon.service;

@Service
@Transactional
@PropertySource(value = "classpath:redis.properties")
public class UserService {
 @Autowired
 private UserRepository userRepository;
 @Autowired
 private JedisClient jedisClient;
 @Value("${REDIS_USER_SESSION_KEY}")
 private String REDIS_USER_SESSION_KEY;
 @Value("${SSO_SESSION_EXPIRE}")
 private Integer SSO_SESSION_EXPIRE;
 
    public Result registerUser(User user) {
     // 检查用户名是否注册,一般在前端验证的时候处理,因为注册不存在高并发的情况,这里再加一层查询是不影响性能的 if (null != userRepository.findByAccount(user.getAccount())) { return Result.build(400, "");
     }
     userRepository.save(user);
     // 注册成功后选择发送邮件激活。现在一般都是短信验证码 return Result.build(200, "");
    }
    
    public Result userLogin(String account, String password,
       HttpServletRequest request, HttpServletResponse response) {
     // 判断账号密码是否正确
  User user = userRepository.findByAccount(account); if(user == null){ return Result.build(400, "账号名或密码错误");
  } if (!CheckUtils.decryptPassword(user, password)) { return Result.build(400, "账号名或密码错误");
  }
  // 生成token
  String token = UUID.randomUUID().toString();
  // 清空密码和盐避免泄漏
  String userPassword = user.getPassword();
  String userSalt = user.getSalt();
  user.setPassword(null);
  user.setSalt(null);
  // 把用户信息写入 redis
  jedisClient.set(REDIS_USER_SESSION_KEY + ":" + token, JsonUtils.objectToJson(user));
  // user 已经是持久化对象了,被保存在了session缓存当中,若user又重新修改了属性值,那么在提交事务时,此时 hibernate对象就会拿当前这个user对象和保存在session缓存中的user对象进行比较,如果两个对象相同,则不会发送update语句,否则,如果两个对象不同,则会发出update语句。
  user.setPassword(userPassword);
  user.setSalt(userSalt);
  // 设置 session 的过期时间
  jedisClient.expire(REDIS_USER_SESSION_KEY + ":" + token, SSO_SESSION_EXPIRE);
  // 添加写 cookie 的逻辑,cookie 的有效期是关闭浏览器就失效。
  CookieUtils.setCookie(request, response, "USER_TOKEN", token);
  // 返回token return Result.ok(token);
 }
    
    public void logout(String token) {
     jedisClient.del(REDIS_USER_SESSION_KEY + ":" + token);
    }

 public Result queryUserByToken(String token) {
  // 根据token从redis中查询用户信息
  String json = jedisClient.get(REDIS_USER_SESSION_KEY + ":" + token);
  // 判断是否为空 if (StringUtils.isEmpty(json)) { return Result.build(400, "此session已经过期,请重新登录");
  }
  // 更新过期时间
  jedisClient.expire(REDIS_USER_SESSION_KEY + ":" + token, SSO_SESSION_EXPIRE);
  // 返回用户信息 return Result.ok(JsonUtils.jsonToPojo(json, User.class));
 }
}

四、Controller 层

负责跳转登录页面跳转,负责用户的登录,退出,获取令牌的操作。UserController.java和PageController.java

package com.itdragon.controller;

@Controller
@RequestMapping("/user")
public class UserController {
 @Autowired
 private UserService userService;
 @RequestMapping(value="/login", method=RequestMethod.POST)
 @ResponseBody
 public Result userLogin(String username, String password,
                            HttpServletRequest request, HttpServletResponse response) {
  try {
   Result result = userService.userLogin(username, password, request, response); return result;
  } catch (Exception e) {
   e.printStackTrace(); return Result.build(500, "");
  }
 }
 
 @RequestMapping(value="/logout/{token}")
 public String logout(@PathVariable String token) {
  userService.logout(token); // 思路是从Redis中删除key,实际情况请和业务逻辑结合 return "back";
 }
 
 @RequestMapping("/token/{token}")
 @ResponseBody
 public Object getUserByToken(@PathVariable String token) {
  Result result = null;
  try {
   result = userService.queryUserByToken(token);
  } catch (Exception e) {
   e.printStackTrace();
   result = Result.build(500, "");
  } return result;
 }
}
package com.itdragon.controller;

@Controller
public class PageController {
 @RequestMapping("/login")
 public String showLogin(String redirect, Model model) {
  model.addAttribute("redirect", redirect); return "login";
 } 
}

五、视图层

一个简单的登录页面和资源展示页面。login.jsp、index.jsp和indexHomePage.jsp

六、Spring 自定义拦截器

这里是另外一个项目 service-test-sso 中的代码,首先在src/main/resources/spring/springmvc.xml 中配置拦截器,设置哪些请求需要拦截

"com.it.controller" />"org.springframework.web.servlet.view.InternalResourceViewResolver">
  "prefix" value="/WEB-INF/views/" />
  "suffix" value=".jsp" />
 "/WEB-INF/static/" mapping="/static/**"/>
 "/indexHomePage/**"/>
   "com.it.interceptors.UserLoginHandlerInterceptor"/>

UserLoginHandlerInterceptor.java

package com.it.interceptors;

public class UserLoginHandlerInterceptor implements HandlerInterceptor {

    public static final String COOKIE_NAME = "USER_TOKEN";
    @Autowired
    private UserService userService;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String token = CookieUtils.getCookieValue(request, COOKIE_NAME);
        User user = this.userService.getUserByToken(token); if (StringUtils.isEmpty(token) || null == user) {
   // 跳转到登录页面,把用户请求的url作为参数传递给登录页面。
   response.sendRedirect("http://localhost:8081/login?redirect=" + request.getRequestURL());
   // 返回false return false;
  }
  // 把用户信息放入Request
  request.setAttribute("user", user);
  // 返回值决定handler是否执行。true:执行,false:不执行。 return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
            Exception ex) throws Exception {
    }

}

七、操作步骤

测试思路:
第一步:注册用户,执行sso 项目下SpringbootStudyApplicationTests.java 单元测试类中的 registerUser() 方法添加用户。
第二步:开启sso服务。
第三步:再开启两个service-test-sso服务。
第四步:在service-test-sso服务页面点击“访问主页”按钮进入权限页面测试。

八、sso项目结构

基于SpringBoot实现单点登录系统

service-test-sso项目结构
基于SpringBoot实现单点登录系统

访问主页
基于SpringBoot实现单点登录系统

点击登录
基于SpringBoot实现单点登录系统
用户表存储如下
基于SpringBoot实现单点登录系统
依次通过访问如下链接:
http://localhost:8083/
http://localhost:8081/login?redirect=/indexHomePage
http://localhost:8082/
然后直接就可以不用登录就可以访问资源了,实现SSO功能


免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读
[通信技术]

java在嵌入式系统设计中有哪些应用方案?

Java是一种广泛应用于软件开发的编程语言,它具有跨平台、面向对象和高度可靠性的特点。在嵌入式系统设计中,Java也有着广泛的应用方案。本文将详细介绍Java在嵌入式系统设计中的应用方案,并分析其优势和挑战。

关键字: Java 软件开发 编程语言
[嵌入式分享]

嵌入式系统中java的开发工具有哪些?有哪些解决方案?

嵌入式系统是指集成了计算机软硬件的特定系统,通常用于控制和监控设备、机器和系统。Java作为一种通用的编程语言,在嵌入式系统的开发中也有广泛的应用。下面将介绍一些嵌入式系统中Java的开发工具和解决方案。

关键字: 嵌入式 计算机 Java
[模拟技术]

Java语言是什么?如何练习基本语法?

Java语言是一种面向对象的编程语言,由Sun Microsystems(现在是Oracle Corporation)于1995年推出。Java具有跨平台性和可移植性的特点,广泛用于开发各种应用程序,包括嵌入式系统、移动...

关键字: Java 编程语言 互联网
[阿里云]

中国唯一代表!阿里第三次当选Java全球管理组织最高管理席位

12月7日消息,近日,Java全球管理组织JCP披露了最高执行委员会(JCP-EC)新成员名单,作为唯一中国代表,阿里巴巴再次连任,任期两年。这是阿里连续三次入选JCP最高管理席位,代表着中国技术公司长期参与Java全球...

关键字: 阿里云 Java
[沉默王二]

2.7万学费的培训机构,体验如何?

经常有一些小伙伴来咨询二哥培训机构方面的问题,通常情况下,如果自学能力可以的话,我是建议通过《Java 程序员进阶之路》配上 B 站的教学视频,先把 Java 后端四大件学扎实(Java 基础、Spring Boot、R...

关键字: Java 培训机构 算法
[单片机]

学习Java的知识点总结

Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向...

关键字: Java C++
[沉默王二]

3000 天,破茧成蝶

作为一个 PHP 转 Java 的业务开发,经常对自己的 Java 基础是否扎实有个问号。实际业务上由于基本上都是 CURD,并没遇到什么困难,但越是这样有时候心里就越慌。

关键字: PHP Java
[沉默王二]

别再用 System.currentTimeMillis 统计耗时了,太LOW,这个工具类好用到爆!

昨天,二哥的编程星球里的一位球友问我能不能给他解释一下@SpringBootApplication注解是什么意思,还有 Spring Boot 的运行原理,于是我就带着他扒拉了一下这个注解的源码,还有SpringAppl...

关键字: Java Spring Boot
[《机电信息》]

基于Java和selenium的自动化操作工具的设计与实现

摘要:设计了一种基于Java和selenium的自动化操作工具,利用selenium开源自动化测试框架建立企业信息系统自动化执行的解决方案,着重对比自动化工具和手工操作的差异。通过使用工具和人工操作的对比试验,证明这种工...

关键字: 自动化操作工具 selenium Java
[《物联网技术》杂志]

油气生产物联网管理子系统的设计与实现

摘 要 :为了节约油气生产的建设投资成本,提高劳动生产效率,中石油建设了规范、统一的油气生产物联网系统。文中介绍了油气生产物联网管理子系统的功能设计、系统数据架构及数据处理。该系统的持续完善与深入应用必将为转变油气田生产...

关键字: 油气生产 物联网 管理 Java 生产效率 子系统

架构师社区

1737 篇文章

关注

发布文章

厂商专栏

  • 厂商文章

    厂商文章

    5245篇文章
  • 贸泽电子

    贸泽电子

    793篇文章
  • 意法半导体

    意法半导体

    734篇文章
  • ADI

    ADI

    728篇文章
  • 英飞凌

    英飞凌

    456篇文章
  • 是德科技

    是德科技

    316篇文章

热门文章

  • 为什么一打开相机就杀后台?内存和存储为何重要?
  • 人工智能、5G以及物联网,数字化转型的关键动力
  • 打造工业级产品解决方案为何如此重要
  • 又一大厂退出中国大陆半导体制造业务!
  • MP3、DVD等几乎消失!下一个被淘汰的电子产品是什么
  • 华为Pura70可一键消除衣服,华为紧急禁用!
  • 华为发布乾崑ADS 3.0智驾系统 AEB评测全面领先
  • 中软国际两年砍掉2.2万人,此前被曝多次裁员!
  • 裁员换帅!停用中国芯片致出货腰斩?
  • 美国正考虑新一轮制裁,全与这家公司有关!
  • 新一代4D成像雷达性能出众,价格合理
  • 国产自主AI算力!华为:今年将发展超50家鲲鹏和昇腾伙伴
  • 中国工程师在美国被捕:涉嫌TPU/GPU泄密!

编辑精选

更多

论坛活动

  • 泰克全新4系列B MSO,与各种测试挑战say goodbye
    泰克全新4系列B MSO,与各种测试挑战say goodbye
  • 【奖励升级】21ic下载“上传有红包”邀请函,请查收~
  • 欧姆龙器件与模块解决方案,为新能源和高速通信的普及做贡献
  • 全新STM32U0系列MCU来袭, 打造功耗、功能与成本的完美平衡
更多

论坛热帖

  • 十大技术帖
  • 十大生活帖
  • 逆变器上电打火
  • ModusToolBox导入工程详细步骤
  • 请问怎么处理三极管输出信号的陡峭问题?
  • 请问WCH-BLE 分析仪 捕获、分析2.4G的通信包吗
  • Wi-Fi入门学习资料分享
  • 创建新例程,编译通过,但有错误
  • 趋肤效应
  • 编译出错的问题
  • 玩具被拆了以后装不回去了 请老师们帮忙
  • 一个仍在网上流传的奇葩逆变电路
  • 发小
  • 出差补助的那些事
  • 冰棍、雪糕
  • 太阳下的暴雨
  • 现在是越来越看不懂,小麦亩产居然2500斤了,我也是很震惊
  • 今天来场超长的酒局
  • 如何看待现在晚婚甚至不婚 ?
  • 紫光展锐增速翘首
  • 人能弘道
  • 如何看待现在晚婚甚至不婚

技术子站

更多

资料下载

  • 明纬开关电源S-350-27改可调0-95V 0-7A教程
  • 显示模组 瀚彩1.44+ST7735 SPI演示程序STM32(Keil)
  • stm32 320240 点阵屏驱动
  • FT_Prog_v3.12.38.643--FTD USB 工作模式设定及eprom读写
  • 信号完整性基础知识,从仿真到测量
  • 出租车计价器使用keil4实现
  • 硬件基础(电阻_电容_电感_二极管_三极管_运放)
  • stm32参考程序洗衣机源代码实验例程
  • stm32参考程序手持医疗项目代码(QT+arm)实验例程
  • Python操作基本语法
更多

技术学院

  • 虚拟内存有哪些作用?虚拟内存对游戏流畅度是否有影响?
  • 虚拟内存有哪些类型?虚拟内存有哪些好处?
  • 如何设置虚拟内存?虚拟内存是否可以关掉?
  • TrendForce集邦咨询:HBM3e产出放量带动,2024年底HBM投片量预估占先进制程比重35%
  • 哪些原因致使电缆老化?电缆常见故障有哪些?
  • 电缆有哪些安全规范?如何检测电缆的质量?
  • 你了解电缆吗?电缆有哪些工艺特性?
  • 什么是谐波减速机?什么是行星减速机?二者有何区别?
关闭
关闭

4617作文网梦见不吉利的梦怎么破解方法周公解梦孕妇梦见死去的亲人电脑免费起名日用品起店名项氏起名酒店的姓名酒店起名大全公司起名软件排行榜算命测名字周易姓名预测余姓女儿起名字酒店起名字大全免费吗精灵宝可梦单机破解观看想起一个名字八字算命周易给灯笼起个好听的名字周易取名字测试题倍字起名意义周易警句取名周公解梦全集下载免费胡宝宝在线起名大全姓秦女起名梦见4周公解梦属蛇八字算命菲梦少女破解版游戏海之梦online内购破解周公解梦梦见自己穿白衣服周易起名打分测试免费纺织起名五行缺金女儿起名用字琪和琦哪个起名好淀粉肠小王子日销售额涨超10倍罗斯否认插足凯特王妃婚姻让美丽中国“从细节出发”清明节放假3天调休1天男子给前妻转账 现任妻子起诉要回网友建议重庆地铁不准乘客携带菜筐月嫂回应掌掴婴儿是在赶虫子重庆警方辟谣“男子杀人焚尸”国产伟哥去年销售近13亿新的一天从800个哈欠开始男孩疑遭霸凌 家长讨说法被踢出群高中生被打伤下体休学 邯郸通报男子持台球杆殴打2名女店员被抓19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警两大学生合买彩票中奖一人不认账德国打算提及普京时仅用姓名山西省委原副书记商黎光被逮捕武汉大学樱花即将进入盛花期今日春分张家界的山上“长”满了韩国人?特朗普谈“凯特王妃P图照”王树国3次鞠躬告别西交大师生白宫:哈马斯三号人物被杀代拍被何赛飞拿着魔杖追着打315晚会后胖东来又人满为患了房客欠租失踪 房东直发愁倪萍分享减重40斤方法“重生之我在北大当嫡校长”槽头肉企业被曝光前生意红火手机成瘾是影响睡眠质量重要因素考生莫言也上北大硕士复试名单了妈妈回应孩子在校撞护栏坠楼网友洛杉矶偶遇贾玲呼北高速交通事故已致14人死亡西双版纳热带植物园回应蜉蝣大爆发男孩8年未见母亲被告知被遗忘张立群任西安交通大学校长恒大被罚41.75亿到底怎么缴沈阳一轿车冲入人行道致3死2伤奥运男篮美国塞尔维亚同组周杰伦一审败诉网易国标起草人:淀粉肠是低配版火腿肠外国人感慨凌晨的中国很安全男子被流浪猫绊倒 投喂者赔24万杨倩无缘巴黎奥运男子被猫抓伤后确诊“猫抓病”春分“立蛋”成功率更高?记者:伊万改变了国足氛围奥巴马现身唐宁街 黑色着装引猜测

4617作文网 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化