单点登录系统

根据存储cookie来进行判断。实现单点登录。以及基于redis进行缓存数据

一、创建SSO登录页面,进行其他服务时都会跳转到登陆页面中

在idea中创建 login.html     登陆页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Login Module</title>
</head>
<body>
<h1>欢迎来到登陆页面</h1>
<p style="color: red;" th:text="${session.msg}"></p>
<form action="/ds/login" method="POST" accept-charset="UTF-8">
    用户名:<input name="username" value="" >
    密码:<input name="password" value="" >
    <button type="submit">登录</button>
</form>
</body>
</html>

在idea中创建 logout.html     退出页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>退出成功</title>
</head>
<body>
<h1>退出成功,欢迎下次光临</h1>
</body>
</html>

SSO单点项目依赖

<?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>com.jt</groupId>
    <artifactId>sso-wy1</artifactId>
<!--    打包时需要的依赖-->
    <packaging>jar</packaging>

    <version>1.0-SNAPSHOT</version>
  <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!--spring boot 核心依赖版本定义(spring官方定义)-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR9</version>
                <type>pom</type>
                <scope>import</scope><!--引入三方依赖的版本设计-->
            </dependency>

            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.6.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

        <!--添加Sentinel依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>0.9.0.RELEASE</version>
        </dependency>

        <!--    基于alibaba的nacos实现服务注册管理,当添加了此依赖后,系统启动时 会向nacos
                服务发送一些心跳包,进行服务注册 一般5秒会发送一次。服务的注册和发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--        添加feign组件-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--        添加nacos配置中心依赖,此依赖添加后,项目会从nacos获取配置信息-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>




        <!--redis应用依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>


        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
        <!--mybatis依赖包-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>


        <!--lombok 依赖,子工程中假如需要lombok,不需要再引入-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope><!--provided 表示此依赖仅在编译阶段有效-->
        </dependency>
        <!--单元测试依赖,子工程中需要单元测试时,不需要再次引入此依赖了-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <!--            <scope>test</scope>&lt;!&ndash;test表示只能在test目录下使用此依赖&ndash;&gt;-->
            <exclusions>
                <exclusion><!--排除一些不需要的依赖-->
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.60</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <!--其它依赖...-->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.9</version>
        </dependency>

    </dependencies>


    <build>
        <plugins>
            <!--通过maven-compiler-plugin插件设置项目
            的统一的jdk编译和运行版本-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>

                <configuration>
                    <includeSystemScope>true</includeSystemScope>
                    <mainClass>com.jt.ssoAppcation</mainClass>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>

        </plugins>
    </build>
    
</project>

application.yml  配置

#配置端口号
server:
  port: 8091

#管理数据源
spring:
  datasource:
    #高版本驱动使用
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/sso?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    #设定用户名和密码
    username: root
    password: root
    #链接-----redis
  redis:
    host: 192.168.232.126
    port: 6379



#SpringBoot整合Mybatis
mybatis:
  #指定别名包
  type-aliases-package: com.jt.pojo
  #扫描指定路径下的映射文件
  mapper-locations: classpath:/mapper/*.xml
  #开启驼峰映射
  configuration:
    map-underscore-to-camel-case: true
  # 一二级缓存默认开始 所以可以简化

#打印mysql日志
logging:
  level:
    com.jt.mapper: debug

创建认证中心controller

package com.jt.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.jt.mapper.UserMapper;
import com.jt.pojo.User;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Map;
import java.util.UUID;



/**
 * sso 认证中心  前端调用 先走完认证中心
 */
@Slf4j
@Controller
@RequestMapping("/ds")
public class qqqqController {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @PostMapping("/login") //从页面获取账号密码  判断没有token时跳转到登陆页面
    public String login(HttpSession session,HttpServletRequest request,HttpServletResponse response) {
//        String userName = jsonObject.get("username").toString();
//        String password = jsonObject.get("password").toString();
        String userName = "admin";
        String password = "admin";

        try {
            //获取重定向的页面
            String target = session.getAttribute("target").toString();
            Map<String, String> user = userMapper.findByUserName(userName);// 根据账号获取信息
            String name = user.get("username");
            String word = user.get("password");
            if (password.equals(word)) {

                //添加到----------------redis------------------服务器redis
                String token = UUID.randomUUID().toString();
                redisTemplate.opsForValue().set(token,user.toString()); //将用户信息存入redis中


                //需要添加时效 -----添加cookie-----------------客户端  要是再子系统间访问的话,域是必须相同的
                Cookie c = new Cookie("ssoToken", token.toString());// 新建一个Cookie对象
                //c.setDomain("localhost");
                c.setPath("/");
                response.addCookie(c);//通过response响应数据
                String value = c.getValue();
                System.out.println("valueA = " + value);

                return "redirect:" + target;//重定向地址,target地址 前面的字符串必须写对,否则报错

            } else {
                //登陆失败
                session.setAttribute("msg","用户名或密码错误");
                return "login";
            }
        }catch (NullPointerException e){
            String target = "http://localhost:8092/wy/index";
            return "redirect:" + target;
        }

    }

    
    /**
     * 校验token
     *
     * @param
     * @return
     */
    @GetMapping("/info")   //点击任何页面功能时,先调用此方法进行验证token   此处将token传过来
    @ResponseBody
    public ResponseEntity<User> checkToken(String token) throws JsonProcessingException {

        if (!StringUtils.isEmpty(token)){
            try {
                String s = redisTemplate.opsForValue().get(token).toString();
                System.out.println("s = " + s);
            }catch (NullPointerException e){
                User user = new User("admin","admin");//此处获取用户信息 此处简写了
                return ResponseEntity.ok(user);
            }

            User user = new User("admin","admin");
            return ResponseEntity.ok(user);
        }
        else {
            return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);//错误的请求,要token没得到
        }
    }
}

创建sso页面跳转逻辑controller


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/***
 *  页面跳转逻辑
 */


@Controller
@RequestMapping("/view")
public class ViewController {

    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("/login")
    public String tologin(@RequestParam(required = false,defaultValue = "") String target
                          , HttpSession session,
                          @CookieValue(required = false,value = "ssoToken")Cookie cookie,
                          HttpServletResponse response) {

        if (StringUtils.isEmpty(target)){
            target = "http://localhost:8092/wy/index";//如果为空设定地址到首页
        }
        //如果是已经登陆的用户再次访问登录系统时,就要重定向
        if (cookie!=null){
            String value = cookie.getValue();//获取token
            System.out.println("value = " + value);
            try {
                String s = redisTemplate.opsForValue().get(value).toString();  //没有设置过期时间,后期可以加上
                System.out.println("s = " + s);
                if (s != null){
                    System.out.println("ok");
                    return "redirect:" + target;
                }
            }catch (NullPointerException e){
                session.setAttribute("target",target);
                return "login";//跳转到登陆页面
            }

        }
//        cookie.setMaxAge(0);
//        response.addCookie(cookie);
        //TODO:此处应该有校验地址是否合法的校验
        //重定向的地址
        session.setAttribute("target",target);
        return "login";//跳转到登陆页面
    }



    @GetMapping("/logout")   //清除缓存 token
    public String logout(@CookieValue(required = false,value = "ssoToken")Cookie cookie,
                         HttpServletResponse response) {
        String value = cookie.getValue();
        System.out.println("value = " + value);
        redisTemplate.opsForValue().get(value);
        redisTemplate.delete(value);

        //将cookie的有效期设置为0,命令浏览器删除该cookie
        cookie.setMaxAge(0);
        response.addCookie(cookie);
        System.out.println("cookie = " + cookie);
        return "logout";
    }
}

创建User类,链接数据库,验证登陆用户账号密码信息。

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")
public class User implements Serializable {


    @TableField(value = "username")
    private String username;  //账号
    @TableField(value = "password")
    private String password;  //密码
}
import org.apache.ibatis.annotations.Mapper;

import java.util.List;
import java.util.Map;

@Mapper
public interface UserMapper {

    Map<String, String> findByUserName(String username);
}
<?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="com.jt.mapper.UserMapper">

    <select id="findByUserName" resultType="java.util.Map">
        select username,password from user where username=#{username}
    </select>
</mapper>

启动项目进行网页浏览

二、创建wy服务页面

 创建 index.html     链接跳转到sso登陆页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>wy</title>
</head>
<body>
<h1>欢迎来到wy</h1>
<span>
    <a th:if="${session.loginUser == null}" href="http://localhost:8091/view/login?target=http://localhost:8092/wy/index">登陆</a>
<a href="http://localhost:8091/view/logout">退出登录</a>
</span>
</body>
</html>

启动类中添加 RestTemplate 方法 

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class wyApplication {
    public static void main(String[] args) {
        SpringApplication.run(wyApplication.class, args);
    }


    @LoadBalanced//通过这个注解描述对象。底层会调用一个拦截器并进行负载均衡的算法
    @Bean
    public RestTemplate loadBalanced1() {
        return new RestTemplate();
    }

    @Primary   //在配置类配置RestTemplate时,分别配置负载均衡与默认优先选择,即写两个RestTemplate方法  (因为调用报错)
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

创建wy的controller

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Map;

/**
 * 页面功能一
 */
@Controller
@RequestMapping("/wy")
public class WyController {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RestTemplate restTemplate;//发送http请求的工具


    @Value("${spring.application.name:sso-springbootCS}")
    String appname;//获得执行的服务对象的名字


    @GetMapping("/index")
    public String index(@CookieValue(required = false,value = "ssoToken")Cookie cookie, HttpSession session) {
        
        if (cookie!=null){
            String token = cookie.getValue();
            System.out.println("校验token = " + token);
            if (!StringUtils.isEmpty(token)){

                //通过骨架给其赋值    会自动拦截 并且根据要进行调用的服务的名字匹配端口号和id
                String url = String.format("http://localhost:8091/ds/info?token=",appname);

                //此处发送请求到校验地址 、ds/info 携带token 进行校验
                String result = restTemplate.getForObject(url + token, String.class);

//                session.setAttribute("loginUser",s); 将数据返回给前端  获得登录账号信息
                System.out.println("result = " + result);//查看数据
            }
        }
        return "index";  //返回的原因是要跳转到index页面
    }
}

查看页面

多个服务都可同上 ,此处是两个 sso-wy1    ssp-vip

以上代码完成后进行启动所有的服务。然后进行验证

http://localhost:8092/wy/index

http://localhost:8093/wy/index

当登陆第一个8092,输入账号密码确认后再登陆8093即不会再进行跳转到登陆页面。

如果登陆成功则会跳转到目标页面,target携带目标页面。

进来后,一直点击登陆不会再进行跳转到登陆页面了。因为验证了cookie中的token

当其中一个点击退出时,所有的服务就需要重新登陆才行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值