Spring boot + Redis单点登入SSO

简介
单点登录SSO(Single Sign On)是在一个多系统共存的环境下,用户在一处登录之后,就不用在其他的系统中登录,也就是用户的一次登录能得到其他所有系统的信任.单点登录在大型网站里面使用的比较多,例如像阿里巴巴这样的网站,在万丈的背后是成百上千的子系统,用户一次操作或交易可能涉及到几个十几个子系统的协作,如果每个子系统都需要用户认证,会在无形中增加时间的耗费

结构图
Spring boot + Redis单点登入SSO_第1张图片
application.yml

spring:
  redis:
    database: 0    #redis的默认数据库为0
    host: 139.224.245.96  #链接redis的ip
    port: 6379  #链接redis的端口号
    password:   #链接redis的密码 默认为空
    jedis:
      pool:
        max-total: 200    #链接redis的总数目
        max-active: 100  #链接redis的最大
        max-idle: 8      #最大的链接数量
        min-idle: 5     #最小的链接数量
  datasource:
    url: jdbc:mysql://139.224.245.96:3306/huibo?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    show-sql: true
server:
  port: 8989

REDIS_KEY : uuuu


实体类user

package com.example.demo.pojo;

import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Data
@Entity
@Table(name = "user")
public class user {

    @Id
    @Column(name = "id",unique = true,nullable = false)
    private int id;

    private String name;

    private int password;

}

dao层

package com.example.demo.dao;

import com.example.demo.pojo.user;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserDao extends JpaRepository<user,Integer> {
    public user findByNameAndAndPassword(String username,int password);
}

service层

package com.example.demo.service;

import com.example.demo.dao.UserDao;
import com.example.demo.pojo.user;
import com.example.demo.redis.JedisDao;
import com.example.demo.util.CookieUtils;
import com.example.demo.util.JsonUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private JedisDao jedisDao;

    @Value("${REDIS_KEY}")
    private String REDIS_KEY;

    private Map<Integer,String> userInfo = new HashMap<>();

    public Object getall(){
        String s = RandomStringUtils.randomAlphabetic(10);
        if (jedisDao.getValue(s)!=null){
            return jedisDao.getValue(s);
        }else {
            List<user> userList= userDao.findAll();
            jedisDao.setValue(s, JsonUtils.objectToJson(userList));
            return userList;
        }
    }

    public user userlogin(HttpServletRequest request, HttpServletResponse response, user u){
        //先进行数据库查询一遍
        user us=userDao.findByNameAndAndPassword(u.getName(),u.getPassword());
        //判断us是否为空
        if(us==null){
            return null;
        }
        //定义新的token
        String token="user_"+ UUID.randomUUID().toString();
        //判断map中是否存在该id
        if(!ObjectUtils.isEmpty(userInfo.get(us.getId()))){
            //从map中获得redis中的key
            String oldToken=userInfo.get(us.getId());
            //删除redis中老的值
            jedisDao.delValue(oldToken);
        }
        //将新的的key保存到map中
        userInfo.put(us.getId(),token);
        //将信息存入redis
        jedisDao.setValue(token,JsonUtils.objectToJson(us));
        //设置redis信息过期时间
        // redisTemplate.expire(token,5*60, TimeUnit.MILLISECONDS);
        //将token放入cookie中
        CookieUtils.setCookie(request,response,"USER_TOKEN",token,5*60,true);

        return us;
    }

    public String getUserByToken(HttpServletResponse response, HttpServletRequest request) {
        user us=null;
        //从cookie中取出用户token
        String token=CookieUtils.getCookieValue(request,"USER_TOKEN");
        String s = jedisDao.getValue(token);
        return s;
    }


}

controller层

package com.example.demo.controller;

import com.example.demo.pojo.user;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@Controller
public class usercontroller {

    @Autowired
    private UserService userService;

    @RequestMapping("/getall")
    @ResponseBody
    public Object getall(){
        return userService.getall();
    }

    @GetMapping(value = "/login")
    public String login1(){
        return  "login";
    }
    @GetMapping(value = "/success")
    public String success(){
        return  "index";
    }

    @PostMapping("/doLogin.html")
    public String login(HttpServletResponse response , HttpServletRequest request, user us, Model model){
        try{
            user u = userService.userlogin(request, response, us);
            if(u==null){
                model.addAttribute("error","用户名或密码不匹配");
                return "login";
            }
            return "redirect:/success";
        }catch (Exception e){
            e.printStackTrace();
            model.addAttribute("error","用户名或密码不匹配");
            return "login";
        }
    }
    @GetMapping("/info")
    @ResponseBody
    public Map getUserInfo(HttpServletResponse response , HttpServletRequest request) throws Exception {
        Map<Object,Object> map=new HashMap<>();
        try{

            String s = userService.getUserByToken(response, request);
            if(s!=null){
                map.put("msg",true);
            }else{
                map.put("msg",false);
            }
            return map;
        }catch (Exception e){
            map.put("success","根据token获取用户信息失败");
            return map;
        }
    }

}

redis类

package com.example.demo.redis;

public interface JedisDao {

    //查
    public String getValue(String key);
    //删
    public Long delValue(String key);
    //增
    public String setValue(String key, String value);
    //设置时间
    public Long expire(String key, int seconds);
}

package com.example.demo.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

@Component
public class JedisDaoImpl implements JedisDao{

    //连接池
    @Autowired
    private JedisPool jedisPool;

    @Override
    public String getValue(String key) {
        Jedis jedis = jedisPool.getResource();
        String value = jedis.get(key);
        jedis.close();
        return value;
    }

    @Override
    public Long delValue(String key) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.del(key);
        jedis.close();
        return result;
    }

    @Override
    public String setValue(String key, String value) {
        Jedis jedis = jedisPool.getResource();
        String str = jedis.set(key, value);
        jedis.close();
        return str;
    }

    @Override
    public Long expire(String key,int seconds) {
        Jedis jedis = jedisPool.getResource();
        Long time = jedis.expire(key, seconds);
        jedis.close();
        return time;
    }
}

util工具类

package com.example.demo.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

@Component
public class JedisDaoImpl implements JedisDao{

    //连接池
    @Autowired
    private JedisPool jedisPool;

    @Override
    public String getValue(String key) {
        Jedis jedis = jedisPool.getResource();
        String value = jedis.get(key);
        jedis.close();
        return value;
    }

    @Override
    public Long delValue(String key) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.del(key);
        jedis.close();
        return result;
    }

    @Override
    public String setValue(String key, String value) {
        Jedis jedis = jedisPool.getResource();
        String str = jedis.set(key, value);
        jedis.close();
        return str;
    }

    @Override
    public Long expire(String key,int seconds) {
        Jedis jedis = jedisPool.getResource();
        Long time = jedis.expire(key, seconds);
        jedis.close();
        return time;
    }
}

package com.example.demo.util;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;

@Configuration
//链接redis 获取appliaction.yml 里的数据以spring.redis开头的方式
@ConfigurationProperties(prefix = "spring.redis")
public class JedisUtil {
    //属性名字和配置文件中必须一致,还要提供get和set方法
    private String host;  //读取到spring.redis.hostg.redis.port
    private int port;//sprin


    @Bean
    public JedisPool jedisPool(){
        JedisPool jedisPool = new JedisPool(host,port);
        System.out.println("已连接:"+host+"上的redis,端口号为:"+port);
        return jedisPool;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }
}

package com.example.demo.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.List;


public class JsonUtils {

    // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();

    /**
     * 将对象转换成json字符串。
     * 

Title: pojoToJson

*

Description:

* @param data * @return */
public static String objectToJson(Object data) { try { String string = MAPPER.writeValueAsString(data); return string; } catch (JsonProcessingException e) { e.printStackTrace(); } return null; } /** * 将json结果集转化为对象 * * @param jsonData json数据 * @param class 对象中的object类型 * @return */ public static <T> T jsonToPojo(String jsonData, Class<T> beanType) { try { T t = MAPPER.readValue(jsonData, beanType); return t; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 将json数据转换成pojo对象list *

Title: jsonToList

*

Description:

* @param jsonData * @param beanType * @return */
public static <T>List<T> jsonToList(String jsonData, Class<T> beanType) { JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType); try { List<T> list = MAPPER.readValue(jsonData, javaType); return list; } catch (Exception e) { e.printStackTrace(); } return null; } }
package com.example.demo.util;

/**
 * 自定义响应类
 * @author lt
 */
public class SimpleResponse {
    private boolean success;
    private Object data;
    private String errCode;
    private String errMsg;
    private String errDesc;

    public SimpleResponse(Object data) {
        this.data = data;
        this.success=true;
    }

    public SimpleResponse(boolean success) {
        this.success = success;
    }

    public SimpleResponse(String errCode, String errMsg, String errDesc) {
        this.errCode = errCode;
        this.errMsg = errMsg;
        this.errDesc = errDesc;
        this.success=false;
    }

    public static SimpleResponse success(boolean success){
        return new SimpleResponse(success);
    }
    public static SimpleResponse success(Object data){
        return new SimpleResponse(data);
    }
    public static SimpleResponse error(String errCode, String errMsg, String errDesc){
        return new SimpleResponse(errCode,errMsg,errDesc);
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public String getErrCode() {
        return errCode;
    }

    public void setErrCode(String errCode) {
        this.errCode = errCode;
    }

    public String getErrMsg() {
        return errMsg;
    }

    public void setErrMsg(String errMsg) {
        this.errMsg = errMsg;
    }

    public String getErrDesc() {
        return errDesc;
    }

    public void setErrDesc(String errDesc) {
        this.errDesc = errDesc;
    }
}

前端登入成功页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>欢迎</h1>
</body>
<script th:src="@{/js/jquery-1.12.4.js}"></script>
<script type="text/javascript">
    $(function () {
        $.get("/info",function (data) {
            if(data.msg==false){
                alert("您的账号已经在别处登录!");
                location.href="/login";
            }
        })
    })
</script>
</html>

登入页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<center>
    <p>登录页面</p>
    <form method="POST" action="doLogin.html">
        <p><label>用户名:</label><input type="text" name="name" id="username"></p>
        <p><label>密码:</label><input type="password" name="password" id="password"></p>
        <p><input type="submit" style="color: blue">&nbsp;&nbsp;&nbsp;<input type="reset"></p>
        <p><span th:text="${error}"></span></p>
    </form>
</center>
</body>
</html>

pom.xml

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.directory.studio/org.apache.commons.lang -->
        <dependency>
            <groupId>org.apache.directory.studio</groupId>
            <artifactId>org.apache.commons.lang</artifactId>
            <version>2.6</version>
        </dependency>
        <!--引入thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

实现效果图
Spring boot + Redis单点登入SSO_第2张图片
登入后
Spring boot + Redis单点登入SSO_第3张图片
另外一个浏览器
Spring boot + Redis单点登入SSO_第4张图片
登入成功后
Spring boot + Redis单点登入SSO_第5张图片
返回之前的浏览器刷新
Spring boot + Redis单点登入SSO_第6张图片

你可能感兴趣的:(Spring boot + Redis单点登入SSO)