基于springcloud的Cookie单点登录

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>

需要同时链接redismysql, 其中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成功
这里的成功并没有考虑 安全性, 性能, 注销异常处理等!

另外由于现在使用的代码与当前文档代码略有不同, 所以需要博主找时间把家中电脑里的代码上传后再放出来链接, 如果想着急使用的, 请私信博主,
或评论处表明, 博主可以私发

码云地址: 敬请稍后~

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
Qt音视频编程》是一本介绍Qt框架中音视频编程相关内容的PDF电子书。Qt是一个跨平台的C++应用程序开发框架,提供丰富的工具和功能,可以方便地进行音视频处理和编程。 该书以Qt框架为基础,深入讲解了音视频编程的原理和实践。首先,书中介绍了Qt的基本知识和开发环境的搭建,让读者能够快速熟悉Qt框架。然后,书中重点介绍了音视频的基础知识,包括音频编解码、视频编解码、音视频格式和协议等内容。读者可以通过本书了解不同的音视频编码算法和格式,以及它们在实际应用中的应用场景。 接下来,书中详细介绍了Qt框架中的音视频编程相关类和函数。比如,Qt提供了方便的音视频输入输出类,可以实现音视频的采集和播放功能。此外,Qt还提供了丰富的音视频处理类,如音频混音和音频滤波器等,可以进行音频的特效处理和增强。读者通过学习本书,可以掌握使用Qt框架进行音视频编程的技巧和方法。 最后,书中提供了一些实际案例和项目,帮助读者将理论知识转化为实际应用。这些案例包括音视频播放器、录音机和视频编辑器等,读者可以通过参考这些案例来实践和巩固所学内容。 《Qt音视频编程》是一本系统、全面且实用的音视频编程指南,适用于想要学习使用Qt框架进行音视频编程的开发者和爱好者。通过阅读本书,读者可以掌握Qt框架的音视频编程相关知识,提高自己在音视频领域的编程能力。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不锈钢大铁锤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值