SSO单点登录系统的实现

Myth丶恋晨 2022-05-09 07:40 422阅读 0赞

什么是SSO?

维基百科的解释是:

70

个人理解:

  1. 我们刚学javaweb所写的程序都是一些比较小的用来练手学习的程序,所有的系统都在一个tomcat内完成,在一个tomcatsession可以共享,之后在企业项目都会被拆分成分布式的项目,也就是每个系统都是一个单独的项目,这种情况下session不能共享,那么如何保存用户的登录信息,于是我们就要用到SSO单点登录系统。我们平时用的支付宝,淘宝等阿里的产品,只需要登录一次就可以使用其各个其他产品,这也是单点登陆在大型互联网企业里的应用。即所谓的“一次登录,处处使用”,下面就介绍下单点登录的实现过程。

传统登录流程

70 1

(图片来源黑马程序员教学视频)

单点登陆的流程

70 2

Java语言实现过程

1.首先创建maven工程,然后导入依赖jar包。

  1. <dependencies>
  2. <!-- 单元测试 -->
  3. <dependency>
  4. <groupId>junit</groupId>
  5. <artifactId>junit</artifactId>
  6. <scope>test</scope>
  7. </dependency>
  8. <dependency>
  9. <groupId>org.springframework</groupId>
  10. <artifactId>spring-webmvc</artifactId>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.springframework</groupId>
  14. <artifactId>spring-jdbc</artifactId>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.springframework</groupId>
  18. <artifactId>spring-aspects</artifactId>
  19. </dependency>
  20. <!-- Mybatis -->
  21. <dependency>
  22. <groupId>org.mybatis</groupId>
  23. <artifactId>mybatis</artifactId>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.mybatis</groupId>
  27. <artifactId>mybatis-spring</artifactId>
  28. </dependency>
  29. <!-- 通用Mapper -->
  30. <dependency>
  31. <groupId>com.github.abel533</groupId>
  32. <artifactId>mapper</artifactId>
  33. </dependency>
  34. <!-- MySql -->
  35. <dependency>
  36. <groupId>mysql</groupId>
  37. <artifactId>mysql-connector-java</artifactId>
  38. </dependency>
  39. <dependency>
  40. <groupId>org.slf4j</groupId>
  41. <artifactId>slf4j-log4j12</artifactId>
  42. </dependency>
  43. <!-- jedis依赖包,用来做redis相关的-->
  44. <dependency>
  45. <groupId>redis.clients</groupId>
  46. <artifactId>jedis</artifactId>
  47. <version>2.6.0</version>
  48. </dependency>
  49. <!-- Jackson Json处理工具包 -->
  50. <dependency>
  51. <groupId>com.fasterxml.jackson.core</groupId>
  52. <artifactId>jackson-databind</artifactId>
  53. </dependency>
  54. <!-- 连接池 -->
  55. <dependency>
  56. <groupId>com.jolbox</groupId>
  57. <artifactId>bonecp-spring</artifactId>
  58. </dependency>
  59. <!-- JSP相关 -->
  60. <dependency>
  61. <groupId>jstl</groupId>
  62. <artifactId>jstl</artifactId>
  63. </dependency>
  64. <dependency>
  65. <groupId>javax.servlet</groupId>
  66. <artifactId>servlet-api</artifactId>
  67. <scope>provided</scope>
  68. </dependency>
  69. <dependency>
  70. <groupId>javax.servlet</groupId>
  71. <artifactId>jsp-api</artifactId>
  72. <scope>provided</scope>
  73. </dependency>
  74. <!-- Apache工具组件 -->
  75. <dependency>
  76. <groupId>org.apache.commons</groupId>
  77. <artifactId>commons-lang3</artifactId>
  78. </dependency>
  79. <dependency>
  80. <groupId>org.apache.commons</groupId>
  81. <artifactId>commons-io</artifactId>
  82. </dependency>
  83. <!-- 加密解密的工具 -->
  84. <dependency>
  85. <groupId>commons-codec</groupId>
  86. <artifactId>commons-codec</artifactId>
  87. <version>1.9</version>
  88. </dependency>
  89. <!-- 数据校验 -->
  90. <dependency>
  91. <groupId>org.hibernate</groupId>
  92. <artifactId>hibernate-validator</artifactId>
  93. <version>5.1.3.Final</version>
  94. </dependency>
  95. </dependencies>
  96. <build>
  97. <plugins>
  98. <!-- 配置Tomcat插件 -->
  99. <plugin>
  100. <groupId>org.apache.tomcat.maven</groupId>
  101. <artifactId>tomcat7-maven-plugin</artifactId>
  102. <configuration>
  103. <port>8083</port>
  104. <path>/</path>
  105. </configuration>
  106. </plugin>
  107. </plugins>
  108. </build>

这里的maven继承了一个父工程,里面定义好了各种依赖的版本号,因此子工程里不需要定义了版本号了

2.生成web.xml,编写springmvc入口等等操作。

  1. <context-param>
  2. <param-name>contextConfigLocation</param-name>
  3. <param-value>classpath:spring/applicationContext*.xml</param-value>
  4. </context-param>
  5. <!--Spring的ApplicationContext 载入 -->
  6. <listener>
  7. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  8. </listener>
  9. <!-- 编码过滤器,以UTF8编码 -->
  10. <filter>
  11. <filter-name>encodingFilter</filter-name>
  12. <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  13. <init-param>
  14. <param-name>encoding</param-name>
  15. <param-value>UTF8</param-value>
  16. </init-param>
  17. </filter>
  18. <filter-mapping>
  19. <filter-name>encodingFilter</filter-name>
  20. <url-pattern>/*</url-pattern>
  21. </filter-mapping>
  22. <!-- 配置SpringMVC框架入口 -->
  23. <servlet>
  24. <servlet-name>taotao-sso</servlet-name>
  25. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  26. <init-param>
  27. <param-name>contextConfigLocation</param-name>
  28. <param-value>classpath:spring/taotao-sso-servlet.xml</param-value>
  29. </init-param>
  30. <load-on-startup>1</load-on-startup>
  31. </servlet>
  32. <servlet-mapping>
  33. <servlet-name>taotao-sso</servlet-name>
  34. <url-pattern>*.html</url-pattern>
  35. </servlet-mapping>
  36. <welcome-file-list>
  37. <welcome-file>index.html</welcome-file>
  38. </welcome-file-list>

3.搭建SSM环境的配置环境。

首先是spring的主配置文件applicationContext.xml(加载文件,配置基础扫描包,添加数据源)

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
  3. xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
  7. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
  8. http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
  9. <!-- 使用spring自带的占位符替换功能 -->
  10. <bean
  11. class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  12. <!-- 允许JVM参数覆盖 -->
  13. <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
  14. <!-- 忽略没有找到的资源文件 -->
  15. <property name="ignoreResourceNotFound" value="true" />
  16. <!-- 配置资源文件 -->
  17. <property name="locations">
  18. <list>
  19. <value>classpath:jdbc.properties</value>
  20. <value>classpath:env.properties</value>
  21. <value>classpath:redis.properties</value>
  22. </list>
  23. </property>
  24. </bean>
  25. <!-- 扫描包,将相关的bean注入spring,交由spring管理 -->
  26. <context:component-scan base-package="com.taotao"/>
  27. <!-- 定义数据源 -->
  28. <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource"
  29. destroy-method="close">
  30. <!-- 数据库驱动 -->
  31. <property name="driverClass" value="${jdbc.driverClassName}" />
  32. <!-- 相应驱动的jdbcUrl -->
  33. <property name="jdbcUrl" value="${jdbc.url}" />
  34. <!-- 数据库的用户名 -->
  35. <property name="username" value="${jdbc.username}" />
  36. <!-- 数据库的密码 -->
  37. <property name="password" value="${jdbc.password}" />
  38. <!-- 检查数据库连接池中空闲连接的间隔时间,单位是分,默认值:240,如果要取消则设置为0 -->
  39. <property name="idleConnectionTestPeriod" value="60" />
  40. <!-- 连接池中未使用的链接最大存活时间,单位是分,默认值:60,如果要永远存活设置为0 -->
  41. <property name="idleMaxAge" value="30" />
  42. <!-- 每个分区最大的连接数 -->
  43. <!--
  44. 判断依据:请求并发数
  45. -->
  46. <property name="maxConnectionsPerPartition" value="100" />
  47. <!-- 每个分区最小的连接数 -->
  48. <property name="minConnectionsPerPartition" value="5" />
  49. </bean>
  50. </beans>

配置springmvc整合文件sso-servlet.xml(注解驱动,扫描controller包,视图解析器页面跳转)

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:mvc="http://www.springframework.org/schema/mvc"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  7. http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
  8. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
  9. <!-- 注解驱动 -->
  10. <mvc:annotation-driven>
  11. <!-- 添加jsonp统一支持。 -->
  12. <mvc:message-converters register-defaults="true">
  13. <bean
  14. class="com.taotao.sso.util.CallBackMappingJackson2HttpMessageConvert">
  15. <property name="callbackName" value="callback" />
  16. </bean>
  17. </mvc:message-converters>
  18. </mvc:annotation-driven>
  19. <!-- 扫描包 -->
  20. <context:component-scan base-package="com.taotao.sso.controller" />
  21. <!-- 视图解析器 -->
  22. <!-- Example: prefix="/WEB-INF/jsp/", suffix=".jsp", viewname="test" ->
  23. "/WEB-INF/jsp/test.jsp" -->
  24. <bean
  25. class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  26. <property name="prefix" value="/WEB-INF/views/" />
  27. <property name="suffix" value=".jsp" />
  28. </bean>
  29. </beans>

配置spring事务管理context-transaction.xml(事务管理器,策略,增强切入点)

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
  3. xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
  7. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
  8. http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
  9. <!-- 定义事务管理器 -->
  10. <bean id="transactionManager"
  11. class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  12. <property name="dataSource" ref="dataSource" />
  13. </bean>
  14. <!-- 定义事务策略 -->
  15. <tx:advice id="txAdvice" transaction-manager="transactionManager">
  16. <tx:attributes>
  17. <!--所有以query开头的方法都是只读的 -->
  18. <tx:method name="query*" read-only="true" />
  19. <!--其他方法使用默认事务策略 -->
  20. <tx:method name="*" />
  21. </tx:attributes>
  22. </tx:advice>
  23. <aop:config>
  24. <!--pointcut元素定义一个切入点,execution中的第一个星号 用以匹配方法的返回类型,
  25. 这里星号表明匹配所有返回类型。 com.abc.dao.*.*(..)表明匹配cn.itcast.mybatis.service包下的所有类的所有
  26. 方法 -->
  27. <aop:pointcut id="myPointcut" expression="execution(* com.taotao.sso.service.*.*(..))" />
  28. <!--将定义好的事务处理策略应用到上述的切入点 -->
  29. <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" />
  30. </aop:config>
  31. </beans>

配置spring和redis整合文件spring-redis.xml(redis集群配置,连接池配置)

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
  3. xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
  7. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
  8. http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
  9. <!-- 集群的配置 -->
  10. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
  11. <!-- 最大连接数 -->
  12. <property name="maxTotal" value="${redis.maxTotal}" />
  13. </bean>
  14. <!-- 集群连接池 -->
  15. <bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">
  16. <constructor-arg index="0" ref="jedisPoolConfig" />
  17. <constructor-arg index="1">
  18. <list>
  19. <bean class="redis.clients.jedis.JedisShardInfo">
  20. <constructor-arg index="0" value="${redis.node1.host}"/>
  21. <constructor-arg index="1" value="${redis.node1.port}"/>
  22. </bean>
  23. </list>
  24. </constructor-arg>
  25. </bean>
  26. </beans>

配置spring和mybaits整合文件spring-mybatis(配置sqlSessionFactory,mapper扫描代理)

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
  3. xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
  7. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
  8. http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
  9. <!-- 定义sqlSessionFactory -->
  10. <bean class="org.mybatis.spring.SqlSessionFactoryBean">
  11. <!-- 配置数据源 -->
  12. <property name="dataSource" ref="dataSource"/>
  13. <!-- 配置mybatis的全局配置文件 -->
  14. <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
  15. <!-- 配置mapper.xml文件扫描路径 -->
  16. <!-- <property name="mapperLocations" value="classpath:mybatis/mappers/**/*.xml"/> -->
  17. <!-- 配置别名包 -->
  18. <property name="typeAliasesPackage" value="com.taotao.sso.pojo"/>
  19. </bean>
  20. <!-- 配置mapper接口扫描包 -->
  21. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  22. <property name="basePackage" value="com.taotao.sso.mapper"/>
  23. </bean>
  24. </beans>

4.编写dao层,这里使用的是通用mapper,直接继承通用mapper的Mapper接口类就行了。就省略了,通用mapper的用法前几期文章有介绍过有兴趣可以去看看。

5.编写service层。

  1. package com.taotao.sso.service.impl;
  2. import org.apache.commons.codec.digest.DigestUtils;
  3. import org.apache.commons.lang3.StringUtils;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Service;
  6. import com.fasterxml.jackson.databind.ObjectMapper;
  7. import com.taotao.sso.mapper.UserMapper;
  8. import com.taotao.sso.pojo.User;
  9. import com.taotao.sso.service.RedisService;
  10. import com.taotao.sso.service.UserService;
  11. @Service("userService")
  12. public class UserServiceImpl implements UserService {
  13. @Autowired
  14. private UserMapper userMapper;
  15. @Autowired
  16. private RedisService redisService;
  17. private static final ObjectMapper MAPPER=new ObjectMapper();
  18. @Override
  19. public String doLogin(String username, String password) throws Exception {
  20. User record=new User();
  21. record.setUsername(username);
  22. User user=this.userMapper.selectOne(record);
  23. if(null == user){
  24. return null;
  25. }
  26. if(! StringUtils.equals(DigestUtils.md5Hex(password), user.getPassword())){
  27. return null;
  28. }
  29. //登录成功
  30. String token=DigestUtils.md5Hex(System.currentTimeMillis()+username);
  31. this.redisService.set("token"+token, MAPPER.writeValueAsString(user),60*30);
  32. return token;
  33. }
  34. }

redisService和一些CookieUtils工具类

  1. package com.taotao.sso.service;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Service;
  4. import redis.clients.jedis.ShardedJedis;
  5. import redis.clients.jedis.ShardedJedisPool;
  6. @Service
  7. public class RedisService {
  8. @Autowired(required = false)//运行的Spring环境中如果存在就注入,没有就忽略
  9. private ShardedJedisPool shardedJedisPool;
  10. private <T> T execute(Function<T, ShardedJedis> fun) {
  11. ShardedJedis shardedJedis = null;
  12. try {
  13. // 从连接池中获取到jedis分片对象
  14. shardedJedis = shardedJedisPool.getResource();
  15. return fun.callback(shardedJedis);
  16. } catch (Exception e) {
  17. e.printStackTrace();
  18. } finally {
  19. if (null != shardedJedis) {
  20. // 关闭,检测连接是否有效,有效则放回到连接池中,无效则重置状态
  21. shardedJedis.close();
  22. }
  23. }
  24. return null;
  25. }
  26. /**
  27. * 执行set操作
  28. *
  29. * @param key
  30. * @param value
  31. * @return
  32. */
  33. public String set(final String key, final String value) {
  34. return this.execute(new Function<String, ShardedJedis>() {
  35. @Override
  36. public String callback(ShardedJedis e) {
  37. return e.set(key, value);
  38. }
  39. });
  40. }
  41. /**
  42. * 执行GET操作
  43. *
  44. * @param key
  45. * @return
  46. */
  47. public String get(final String key) {
  48. return this.execute(new Function<String, ShardedJedis>() {
  49. @Override
  50. public String callback(ShardedJedis e) {
  51. return e.get(key);
  52. }
  53. });
  54. }
  55. /**
  56. * 执行DEL操作
  57. *
  58. * @param key
  59. * @return
  60. */
  61. public Long del(final String key) {
  62. return this.execute(new Function<Long, ShardedJedis>() {
  63. @Override
  64. public Long callback(ShardedJedis e) {
  65. return e.del(key);
  66. }
  67. });
  68. }
  69. /**
  70. * 设置生存时间,单位为秒
  71. *
  72. * @param key
  73. * @param seconds
  74. * @return
  75. */
  76. public Long expire(final String key, final Integer seconds) {
  77. return this.execute(new Function<Long, ShardedJedis>() {
  78. @Override
  79. public Long callback(ShardedJedis e) {
  80. return e.expire(key, seconds);
  81. }
  82. });
  83. }
  84. /**
  85. * 执行set操作并且设置生存时间,单位为秒
  86. *
  87. * @param key
  88. * @param value
  89. * @return
  90. */
  91. public String set(final String key, final String value, final Integer seconds) {
  92. return this.execute(new Function<String, ShardedJedis>() {
  93. @Override
  94. public String callback(ShardedJedis e) {
  95. String str = e.set(key, value);
  96. e.expire(key, seconds);
  97. return str;
  98. }
  99. });
  100. }
  101. }
  102. package com.taotao.sso.service;
  103. public interface Function<T, E> {
  104. public T callback(E e);
  105. }
  106. package com.taotao.sso.util;
  107. import java.io.UnsupportedEncodingException;
  108. import java.net.URLDecoder;
  109. import java.net.URLEncoder;
  110. import javax.servlet.http.Cookie;
  111. import javax.servlet.http.HttpServletRequest;
  112. import javax.servlet.http.HttpServletResponse;
  113. import org.slf4j.Logger;
  114. import org.slf4j.LoggerFactory;
  115. /**
  116. *
  117. * Cookie 工具类
  118. *
  119. */
  120. public final class CookieUtils {
  121. protected static final Logger logger = LoggerFactory.getLogger(CookieUtils.class);
  122. /**
  123. * 得到Cookie的值, 不编码
  124. *
  125. * @param request
  126. * @param cookieName
  127. * @return
  128. */
  129. public static String getCookieValue(HttpServletRequest request, String cookieName) {
  130. return getCookieValue(request, cookieName, false);
  131. }
  132. /**
  133. * 得到Cookie的值,
  134. *
  135. * @param request
  136. * @param cookieName
  137. * @return
  138. */
  139. public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
  140. Cookie[] cookieList = request.getCookies();
  141. if (cookieList == null || cookieName == null) {
  142. return null;
  143. }
  144. String retValue = null;
  145. try {
  146. for (int i = 0; i < cookieList.length; i++) {
  147. if (cookieList[i].getName().equals(cookieName)) {
  148. if (isDecoder) {
  149. retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
  150. } else {
  151. retValue = cookieList[i].getValue();
  152. }
  153. break;
  154. }
  155. }
  156. } catch (UnsupportedEncodingException e) {
  157. logger.error("Cookie Decode Error.", e);
  158. }
  159. return retValue;
  160. }
  161. /**
  162. * 得到Cookie的值,
  163. *
  164. * @param request
  165. * @param cookieName
  166. * @return
  167. */
  168. public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
  169. Cookie[] cookieList = request.getCookies();
  170. if (cookieList == null || cookieName == null) {
  171. return null;
  172. }
  173. String retValue = null;
  174. try {
  175. for (int i = 0; i < cookieList.length; i++) {
  176. if (cookieList[i].getName().equals(cookieName)) {
  177. retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
  178. break;
  179. }
  180. }
  181. } catch (UnsupportedEncodingException e) {
  182. logger.error("Cookie Decode Error.", e);
  183. }
  184. return retValue;
  185. }
  186. /**
  187. * 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码
  188. */
  189. public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
  190. String cookieValue) {
  191. setCookie(request, response, cookieName, cookieValue, -1);
  192. }
  193. /**
  194. * 设置Cookie的值 在指定时间内生效,但不编码
  195. */
  196. public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
  197. String cookieValue, int cookieMaxage) {
  198. setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
  199. }
  200. /**
  201. * 设置Cookie的值 不设置生效时间,但编码
  202. */
  203. public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
  204. String cookieValue, boolean isEncode) {
  205. setCookie(request, response, cookieName, cookieValue, -1, isEncode);
  206. }
  207. /**
  208. * 设置Cookie的值 在指定时间内生效, 编码参数
  209. */
  210. public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
  211. String cookieValue, int cookieMaxage, boolean isEncode) {
  212. doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
  213. }
  214. /**
  215. * 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)
  216. */
  217. public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
  218. String cookieValue, int cookieMaxage, String encodeString) {
  219. doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
  220. }
  221. /**
  222. * 删除Cookie带cookie域名
  223. */
  224. public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,
  225. String cookieName) {
  226. doSetCookie(request, response, cookieName, "", -1, false);
  227. }
  228. /**
  229. * 设置Cookie的值,并使其在指定时间内生效
  230. *
  231. * @param cookieMaxage cookie生效的最大秒数
  232. */
  233. private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
  234. String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
  235. try {
  236. if (cookieValue == null) {
  237. cookieValue = "";
  238. } else if (isEncode) {
  239. cookieValue = URLEncoder.encode(cookieValue, "utf-8");
  240. }
  241. Cookie cookie = new Cookie(cookieName, cookieValue);
  242. if (cookieMaxage > 0)
  243. cookie.setMaxAge(cookieMaxage);
  244. if (null != request)// 设置域名的cookie
  245. cookie.setDomain(getDomainName(request));
  246. cookie.setPath("/");
  247. response.addCookie(cookie);
  248. } catch (Exception e) {
  249. logger.error("Cookie Encode Error.", e);
  250. }
  251. }
  252. /**
  253. * 设置Cookie的值,并使其在指定时间内生效
  254. *
  255. * @param cookieMaxage cookie生效的最大秒数
  256. */
  257. private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
  258. String cookieName, String cookieValue, int cookieMaxage, String encodeString) {
  259. try {
  260. if (cookieValue == null) {
  261. cookieValue = "";
  262. } else {
  263. cookieValue = URLEncoder.encode(cookieValue, encodeString);
  264. }
  265. Cookie cookie = new Cookie(cookieName, cookieValue);
  266. if (cookieMaxage > 0)
  267. cookie.setMaxAge(cookieMaxage);
  268. if (null != request)// 设置域名的cookie
  269. cookie.setDomain(getDomainName(request));
  270. cookie.setPath("/");
  271. response.addCookie(cookie);
  272. } catch (Exception e) {
  273. logger.error("Cookie Encode Error.", e);
  274. }
  275. }
  276. /**
  277. * 得到cookie的域名
  278. */
  279. private static final String getDomainName(HttpServletRequest request) {
  280. String domainName = null;
  281. String serverName = request.getRequestURL().toString();
  282. if (serverName == null || serverName.equals("")) {
  283. domainName = "";
  284. } else {
  285. serverName = serverName.toLowerCase();
  286. serverName = serverName.substring(7);
  287. final int end = serverName.indexOf("/");
  288. serverName = serverName.substring(0, end);
  289. final String[] domains = serverName.split("\\.");
  290. int len = domains.length;
  291. if (len > 3) {
  292. // www.xxx.com.cn
  293. domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
  294. } else if (len <= 3 && len > 1) {
  295. // xxx.com or xxx.cn
  296. domainName = "." + domains[len - 2] + "." + domains[len - 1];
  297. } else {
  298. domainName = serverName;
  299. }
  300. }
  301. if (domainName != null && domainName.indexOf(":") > 0) {
  302. String[] ary = domainName.split("\\:");
  303. domainName = ary[0];
  304. }
  305. return domainName;
  306. }
  307. }
  308. package com.taotao.sso.util;
  309. import java.io.IOException;
  310. import javax.servlet.http.HttpServletRequest;
  311. import org.apache.commons.io.IOUtils;
  312. import org.apache.commons.lang3.StringUtils;
  313. import org.springframework.http.HttpOutputMessage;
  314. import org.springframework.http.converter.HttpMessageNotWritableException;
  315. import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
  316. import org.springframework.web.context.request.RequestContextHolder;
  317. import org.springframework.web.context.request.ServletRequestAttributes;
  318. import com.fasterxml.jackson.core.JsonEncoding;
  319. import com.fasterxml.jackson.core.JsonProcessingException;
  320. public class CallBackMappingJackson2HttpMessageConvert extends MappingJackson2HttpMessageConverter {
  321. //做jsonP统一支持标识
  322. private String callbackName;
  323. public String getCallbackName() {
  324. return callbackName;
  325. }
  326. public void setCallbackName(String callbackName) {
  327. this.callbackName = callbackName;
  328. }
  329. @Override
  330. protected void writeInternal(Object object, HttpOutputMessage outputMessage)
  331. throws IOException, HttpMessageNotWritableException {
  332. HttpServletRequest request=((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
  333. String callbackParam=request.getParameter(callbackName);
  334. if(StringUtils.isEmpty(callbackParam)){
  335. super.writeInternal(object, outputMessage);//调用父类方法直接返回。
  336. }else{
  337. JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
  338. try {
  339. String result =callbackParam+"("+super.getObjectMapper().writeValueAsString(object)+");";
  340. IOUtils.write(result, outputMessage.getBody(),encoding.getJavaName());
  341. }
  342. catch (JsonProcessingException ex) {
  343. throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
  344. }
  345. }
  346. super.writeInternal(object, outputMessage);
  347. }
  348. }

编写controller层

  1. package com.taotao.sso.controller;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.stereotype.Controller;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import org.springframework.web.bind.annotation.RequestMethod;
  10. import org.springframework.web.bind.annotation.RequestParam;
  11. import com.taotao.sso.service.UserService;
  12. import com.taotao.sso.util.CookieUtils;
  13. @RequestMapping("service/user")
  14. @Controller
  15. public class UserController {
  16. @Autowired
  17. private UserService userService;
  18. /**
  19. * 注册
  20. * @return
  21. */
  22. @RequestMapping(value="/register",method=RequestMethod.GET)
  23. public String register(){
  24. return "register";
  25. }
  26. /**
  27. * 登录
  28. * @return
  29. */
  30. @RequestMapping(value="/login",method=RequestMethod.GET)
  31. public String login(){
  32. return "login";
  33. }
  34. @RequestMapping(value="/doLogin",method=RequestMethod.POST)
  35. public Map<String, Object> doLogin(@RequestParam("username") String userName,
  36. @RequestParam("password")String password,
  37. HttpServletRequest request,HttpServletResponse response){
  38. Map<String, Object> result=new HashMap<>();
  39. try {
  40. String token=this.userService.doLogin(userName, password);
  41. if(token != null && !token.isEmpty()){
  42. result.put("status", 200);
  43. CookieUtils.setCookie(request,response,"token", token);
  44. }else{
  45. result.put("status", 400);//400参数列表错误。
  46. }
  47. } catch (Exception e) {
  48. e.printStackTrace();
  49. result.put("status", 500);
  50. }
  51. return result;
  52. }
  53. }

至此sso系统就写完了。

参考:黑马程序员淘淘商城教学视频。需要资源的可以留言。

发表评论

表情:
评论列表 (有 0 条评论,422人围观)

还没有评论,来说两句吧...

相关阅读

    相关 XXL-SSO 实现SSO登录

    一、 概述: 本文旨在使用XXL-SSO开源架构 实现单点登录系统。 XXL-SSO 是一个分布式单点登录框架、只需要登录一次就可以访问所有相互信任的应用系统。 拥有”

    相关 登录SSO实现原理

    单点登录SSO(Single Sign On)说得简单点就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任。单