Cookie单点登录
说明: 本样例仅使用spring cloud, 而没有集成专门的单点登录框架, 适合高度自定义以及学习原理的同学,
另外如果本demo中有任何错误, 请在下方评论处指正, 感谢~本demo按照博主的思路进行的, 由于思路的变化, 可能会出现前后不一致的情况, 但大部分内容准确无误
服务区分
预估除了eureka之外, 会有两个服务:
1. 用户逻辑处理服务
- 用户页面(登录, 首页)
- 拦截器(cookie校验, session校验)
2. sso登录处理服务
- 登录校验 mysql
- 生成token
- 存储token redis
- 校验token redis
eureka服务的创建这里不做演示
1. 创建sso-server
用于登录校验和token的处理等
1. pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.home</groupId>
<artifactId>sso</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sso</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Edgware.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- 热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>true</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 使用freemarker作为ui -->
<!--<dependency>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter-freemarker</artifactId>-->
<!--</dependency>-->
<!-- 使用feign进行负载均衡调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!-- json -->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.2.3</version>
<classifier>jdk15</classifier>
</dependency>
<!-- redis -->
<!--<dependency>-->
<!--<groupId>redis.clients</groupId>-->
<!--<artifactId>jedis</artifactId>-->
<!--<version>2.7.3</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.data</groupId>-->
<!--<artifactId>spring-data-redis</artifactId>-->
<!--<version>1.7.2.RELEASE</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>RELEASE</version>
</dependency>
<!-- MYSQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 阿里巴巴 druid连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.25</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
需要同时链接redis和mysql, 其中mysql用于校验用户名和密码, redis用于校验token
2. 创建配置文件
application.properties:
server.port=8780
#eureka.instance.hostname=localhost
spring.application.name=sso-server
#eureka server的地址(注册中心)
eureka.client.service-url.defaultZone: http://eurekaserver1:8761/eureka/
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务密码
spring.redis.password=123456
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
#mysql数据库配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/springcloud1?useUnicode=true&characterEncoding=utf-8
spring.datasource.tomcat.validationQuery=SELECT 1 FROM person
#放置断链
spring.datasource.tomcat.test-on-borrow=false
spring.datasource.tomcat.test-while-idle=true
spring.datasource.tomcat.time-between-eviction-runs-millis=18800
#mybatis配置
mybatis.mapper-locations=classpath:mapper/*.xml
同学可以根据自己的需要修改当前服务的端口, 注册中心URL, redis配置, mysql的配置等
3. 在启动程序中添加注解
package org.home.sso;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
@EnableEurekaClient
@MapperScan("org.home.sso.dao")
@EnableAutoConfiguration
public class SsoApplication {
public static void main(String[] args) {
SpringApplication.run(SsoApplication.class, args);
}
}
开启mapper, feign, eureka
注意: @EnableAutoConfiguration不使用的话会造成@mapper注解不好使,我也不知道为什么, 但正常情况下是可以不使用该配置的, 因为 @SpringBootApplication已经包含了该注解
4. 在config包下创建redis配置类
package org.home.sso.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import java.util.Arrays;
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public CacheManager cacheManager(RedisTemplate<?,?> redisTemplate) {
CacheManager cacheManager = new RedisCacheManager(redisTemplate);
return cacheManager;
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(factory);
return stringRedisTemplate;
}
}
没什么好说的, 一些redis的配置, 网上都有
5. 创建mapper
在resource/mapper下创建AccountMapper.xml,用于连接mysql校验用户名密码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.home.sso.dao.AccountDao">
<!-- 测试 -->
<select id="checkUser" parameterType="String" resultType="String">
select password from security_person where username=#{username}
</select>
</mapper>
6. 创建dao, service
(1) AccountDao
package org.home.sso.dao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface AccountDao {
/**
*
* @param username
* @return
*/
public String checkUser(@Param("username") String username);
}
(2) AccountService
package org.home.sso.service;
import java.util.concurrent.TimeUnit;
public interface AccountService {
/**
* redis赋值
* @param key
* @param token
*/
public void set(String key, String token);
/**
* redis获取值
* @param key
* @return
*/
public String get(String key);
/**
*
* @param key
* @param code
*/
public void setCode(String key, String code);
/**
*
* @param key
* @return
*/
public String getCode(String key);
/**
* 向mysql中查询username
* @param username
* @return
*/
public String checkUser(String username);
}
(3) AccountServiceImpl
包括mysql的读取用户名密码方法, redis的存储token和读取token方法
注意: 正常应该讲redis的存储和读取作为工具类抽出来,但是,谁让我懒呢
package org.home.sso.service.impl;
import org.home.sso.dao.AccountDao;
import org.home.sso.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service("AccountServiceImpl")
public class AccountServiceImpl implements AccountService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
AccountDao accountDao;
@Override
public void set(String key, String token) {
redisTemplate.opsForValue().set(key, token);
}
@Override
public String get(String key) {
return (String) redisTemplate.boundValueOps(key).get();
}
@Override
public void setCode(String key, String code) {
stringRedisTemplate.opsForValue().set(key, code, 120, TimeUnit.SECONDS);
}
@Override
public String getCode(String key) {
return stringRedisTemplate.boundValueOps(key).get();
}
@Override
public String checkUser(String username){
return accountDao.checkUser(username);
}
}
7. 创建controller
真正的业务处理类,相当于对外部暴露的接口
主要作用:
- 校验用户,并将成功后的token存储到redis中,设置有效期,返回状态json
- 校验token,判断用户是否处于登录状态,并且更新token,返回状态json
package org.home.sso.controller;
import net.sf.json.JSONObject;
import org.home.sso.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/account")
public class AccountController {
@Autowired
AccountService accountService;
/**
* 校验用户名
* @param username
* @param token
* @param password
* @return
*/
@RequestMapping("/checkUser")
public String checkUser(String username, String token, String password){
//用于存储返回码
Map<String, Object> resultMap = new HashMap<String, Object>();
/*一系列校验*/
String passwordInMysql = accountService.checkUser(username);
if(null == passwordInMysql || "".equals(password)){
//用户不存在
resultMap.put("errcode", "1001");
resultMap.put("success", false);
resultMap.put("message", "用户不存在");
System.out.println("用户" + username + "不存在");
JSONObject jsonObject = JSONObject.fromObject(resultMap);
return jsonObject.toString();
}else{
//用户存在
if (password.equals(passwordInMysql)) {
//账号密码正确
System.out.println("用户" + username + "密码正确:" + password);
//存储redis
// accountService.set(username, token);
//有效期一分钟
accountService.setCode(username, token);
System.out.println("成功存储token:" + token);
//使用setCode,对应要使用getCode
// System.out.println("获取数据:" + accountService.getCode(username));
resultMap.put("errcode", "0");
resultMap.put("success", true);
resultMap.put("message", "成功登陆");
JSONObject jsonObject = JSONObject.fromObject(resultMap);
return jsonObject.toString();
}else {
//密码错误
System.out.println("用户" + username + "密码错误");
resultMap.put("errcode", "1002");
resultMap.put("success", false);
resultMap.put("message", "用户密码错误");
JSONObject jsonObject = JSONObject.fromObject(resultMap);
return jsonObject.toString();
}
}
}
@RequestMapping("/checkToken")
public String checkToken(String username, String token){
//用于存储返回码
Map<String, Object> resultMap = new HashMap<String, Object>();
//查看username在redis的存储值
String tokenInRedis = accountService.getCode(username);
System.out.println("token: " + token);
System.out.println("tokenInRedis: " + tokenInRedis);
if(null != tokenInRedis && !"".equals(tokenInRedis)){
if (token.equals(tokenInRedis)){
//更新
accountService.setCode(username, token);
//登陆成功,将用户名和其他信息全部返回
resultMap.put("errcode", "0");
resultMap.put("success", true);
resultMap.put("message", "用户登陆成功");
resultMap.put("data", username);
JSONObject jsonObject = JSONObject.fromObject(resultMap);
return jsonObject.toString();
}else{
//登陆过期或者被踢下
resultMap.put("errcode", "1001");
resultMap.put("success", false);
resultMap.put("message", "登陆过期或者被踢下");
JSONObject jsonObject = JSONObject.fromObject(resultMap);
return jsonObject.toString();
}
}else{
//未登录或登陆过期
resultMap.put("errcode", "1002");
resultMap.put("success", false);
resultMap.put("message", "未登录或登陆过期");
JSONObject jsonObject = JSONObject.fromObject(resultMap);
return jsonObject.toString();
}
}
}
用户逻辑服务
1. 创建一个web微服务sso-user-client
创建一个springboot程序,引入cloud,web基础依赖,设置基础配置,仿照eureka_client并整合freemarker:
名称: sso_user_client
此处不过多赘述!(到现在你还不会就蹲地上冷静一下, 歇一会别着急)
配置文件:
server.port=8779
#eureka.instance.hostname=localhost
spring.application.name=sso-user-client
#eureka server的地址(注册中心)
eureka.client.service-url.defaultZone: http://eurekaserver1:8761/eureka/
#模板加载路径 按需配置
spring.freemarker.template-loader-path=classpath:/templates/
spring.freemarker.charset=UTF-8
#数字格式化,无小数点
spring.freemarker.settings.number_format='0.##'
2. 创建Feign配置类
由于这里访问rest使用的是feign,所以需要配置feign
在config包下
package org.home.sso.config;
import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static java.util.concurrent.TimeUnit.SECONDS;
/**
* 配置feign声明式调用
*/
@Configuration
public class FeignConfig {
/*
远程调用失败后会重试
*/
@Bean
public Retryer feignRetryer(){
return new Retryer.Default(10000000, SECONDS.toMillis(1), 5);
}
}
3. 配置Feign接口
用于在需要时访问指定的sso服务器url接口
package org.home.sso.feign;
import org.home.sso.config.FeignConfig;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/*
value:远程调用其他服务的服务名
configuration: Feign Client的配置类
*/
@FeignClient(value = "sso-server", configuration = FeignConfig.class)
//该注解自动注入依赖
public interface SsoServerFeign {
@RequestMapping(value = "/account/checkUser", method = RequestMethod.POST)
String checkUser(@RequestParam("username")String username,
@RequestParam("password")String password, @RequestParam("token")String token);
@RequestMapping(value = "/account/checkToken", method = RequestMethod.POST)
String checkToken(@RequestParam("username")String username, @RequestParam("token")String token);
}
主要访问了sso-server微服务的两个接口
4. 创建登录页面和主页
在resources/template下创建login.ftl和index.ftl
login.ftl:
<html>
<head>
<title>登录</title>
</head>
<body>
<form action="/account/login" method="post">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<#--<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">-->
<button type="submit" value="提交">提交</button>
</form>
</body>
</html>
index.ftl:
<html>
<head>
<title>首页</title>
</head>
<body>
<h1>登录成功!</h1>
</body>
</html>
5. 创建controller
主要是处理用户登录跳转,以及访问sso-server进行校验,并返回有效值
package org.home.sso.controller;
import net.sf.json.JSONObject;
import org.home.sso.feign.SsoServerFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
@Controller
@RequestMapping("/account")
public class AccountController {
@Autowired
SsoServerFeign feign;
/**
* 进入登录页面
* @return
*/
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
/**
* 登录校验
* @return
*/
@RequestMapping("/login")
@ResponseBody
public void login(HttpSession session, HttpServletResponse response, String username, String password) throws IOException {
//重定向
ModelAndView mv = new ModelAndView("/account/index");
//生成一个token
String token = getCurrentToken(username);
/*发往SSO服务器一系列验证*/
String result = feign.checkUser(username, password, token);
JSONObject jsonObject = JSONObject.fromObject(result);
Boolean success = (Boolean) jsonObject.get("success");
System.out.println(jsonObject.get("message"));
//登陆成功
if(success){
//存储session
session.setAttribute("username", username);
session.setAttribute("token", token);
System.out.println("存储cookies");
//存储cookie
Cookie cookie_username = new Cookie("username",username);
cookie_username.setDomain("localhost");
cookie_username.setMaxAge(60*60*60);
response.addCookie(cookie_username);
Cookie cookie_token = new Cookie("token",token);
cookie_token.setDomain("localhost");
cookie_token.setMaxAge(60*60*60);
response.addCookie(cookie_token);
//存储需要时间,为了不影响性能,所以在拦截器中先要看session中有没有token,再看cookie
session.setAttribute("token", token);
response.sendRedirect("/account/index");
}else{
//登陆失败
response.sendRedirect("/account/toLogin");
}
}
/**
* 进入首页
* @return
*/
@RequestMapping("/index")
public String index(String token){
return "index";
}
/**
* 拼接一个token返回
* @param username
* @return
*/
public String getCurrentToken(String username){
//获取一个随机数
String uuid = UUID.randomUUID().toString();
//拼接
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String time = sdf.format(new Date());
String token = uuid + "-" + username + "-" + time;
return token;
}
/**
* 将登陆数据,token存储到cookie中
* @param username
* @param token
* @return
*/
public Boolean storageCookie(String username, String token){
return true;
}
}
6. 创建自定义拦截器
需要判断用户是否是登录状态,需要向sso服务器中进行校验:
在interceptor包下创建
package org.home.sso.intercept;
import net.sf.json.JSONObject;
import org.home.sso.feign.SsoServerFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
/**
* 登录状态拦截
*/
@Component
public class TokenCheck implements HandlerInterceptor {
@Autowired
SsoServerFeign feign;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
//获取session
HttpSession session = httpServletRequest.getSession();
//进入登录拦截器
System.out.println("进入登录状态拦截器");
//判断是否有cookie
Map<String, Cookie> cookieMap = ReadCookieMap(httpServletRequest);
//先从session中获取
String tokenSession = (String) session.getAttribute("token");
String usernameSession = (String) session.getAttribute("username");
//先判断session是否有,因为在登陆时存储cookie需要时间
if(null != tokenSession && !"".equals(tokenSession)
&& null != usernameSession && !"".equals(usernameSession)){
System.out.println("session中含有数据,不用从cookie中获取");
/*发往SSO服务器一系列验证*/
String result = feign.checkToken(usernameSession, tokenSession);
JSONObject jsonObject = JSONObject.fromObject(result);
Boolean success = (Boolean) jsonObject.get("success");
//判断是否成功
if(success){
//成功
//获取数据
String data = (String) jsonObject.get("data");
//输出信息
System.out.println((String) jsonObject.get("message"));
//将redis数据放入session中
session.setAttribute("username", data);
session.setAttribute("token", tokenSession);
System.out.println("sso-server校验token成功");
return true;
}else{
//失败
//输出信息
System.out.println((String) jsonObject.get("message"));
httpServletResponse.sendRedirect("/account/toLogin");
return false;
}
}else {
if(null != cookieMap){
System.out.println("含有cookie!");
//获取token
if (cookieMap.containsKey("token") && cookieMap.containsKey("username")) {
//获取token
Cookie cookie_token = (Cookie) cookieMap.get("token");
String token = cookie_token.getValue();
//获取username
Cookie cookie_username = (Cookie) cookieMap.get("username");
String username = cookie_username.getValue();
if (null != token && token.trim() != "") {
System.out.println("获取token");
/*发往SSO服务器一系列验证*/
String result = feign.checkToken(username, token);
JSONObject jsonObject = JSONObject.fromObject(result);
Boolean success = (Boolean) jsonObject.get("success");
//判断是否成功
if(success){
//成功
//获取数据
String data = (String) jsonObject.get("data");
//输出信息
System.out.println((String) jsonObject.get("message"));
//将redis数据放入session中
session.setAttribute("username", data);
session.setAttribute("token", token);
System.out.println("sso-server校验token成功");
return true;
}else{
//失败
//输出信息
System.out.println((String) jsonObject.get("message"));
httpServletResponse.sendRedirect("/account/toLogin");
return false;
}
}else {
System.out.println("没有token");
httpServletResponse.sendRedirect("/account/toLogin");
return false;
}
} else {
System.out.println("没有token");
httpServletResponse.sendRedirect("/account/toLogin");
return false;
}
}else{
System.out.println("没有cookie");
httpServletResponse.sendRedirect("/account/toLogin");
return false;
}
}
//向sso服务器校验token
//判断session是否有值,没有则需要根据返回数据进行存储
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
/**
* 将cookie封装到Map里面
* @param request
* @return
*/
private static Map<String, Cookie> ReadCookieMap(HttpServletRequest request) {
Map<String, Cookie> cookieMap = new HashMap<String, Cookie>();
Cookie[] cookies = request.getCookies();
if (null != cookies) {
for (Cookie cookie : cookies) {
cookieMap.put(cookie.getName(), cookie);
}
}
return cookieMap;
}
}
7. 配置拦截器
需要设定拦截时机等拦截器的配置
在config下创建配置类:
package org.home.sso.config;
import org.home.sso.intercept.TokenCheck;
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.WebMvcConfigurerAdapter;
@Configuration
public class MyConfig extends WebMvcConfigurerAdapter {
@Autowired
TokenCheck tokenCheck;
/**
* 添加一个自定义拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册一个拦截器,并设置拦截目录
registry.addInterceptor(tokenCheck).excludePathPatterns("/account/toLogin")
.excludePathPatterns("/account/login");
}
}
注意: 拦截器需要排除 登录页面 和 登录处理 !
赋值当前这个user的moudle
复制上面的模块, 修改端口, 服务名等
测试效果
两个client,分别端口:8779和8781
进入页面:
http://localhost:8779/account/toLogin
用户名:admin密码:123456,点击登录:
登录成功:
后台展示:
sso-server
sso-user-client:
此时在同一个浏览器中打开另一个微服务的主页,也能成功进入
http://localhost:8781/account/index
在IE浏览器中打开
http://localhost:8781/account/index
会跳转登录页
在IE中登录后,360浏览器刷新页面,又会跳到登录页
(IE登录将360挤下)
至此,单点登录的Demo成功
这里的成功并没有考虑 安全性, 性能, 注销和 异常处理等!
另外由于现在使用的代码与当前文档代码略有不同, 所以需要博主找时间把家中电脑里的代码上传后再放出来链接, 如果想着急使用的, 请私信博主,
或评论处表明, 博主可以私发
码云地址: 敬请稍后~