SpringBoot+SpringSession+Redis(单机)实现无侵入式(不修改现有代码)单点登录(SSO)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37776094/article/details/82291741

1.在pom.xml增加maven源,jar包的版本很重要,很容易掉坑,我使用的都是最新的包

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- spring session 单点登录 -->
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
            <!--<version>2.0.5.RELEASE</version>-->
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>

2.在application.properties文件,添加配置项

server.port=8080
#server.port=8081 //也可使用该配置spring.profiles.active=dev 同时启动两个端口进行测试

# 本地数据库配置
spring.datasource.type= com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name: com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/dd?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.max-idle=10
spring.datasource.max-wait=10000
spring.datasource.min-idle=5
spring.datasource.initial-size=5

#mybatis
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.dd.mapper

#pagehelper
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
pagehelper.params=count=countSql

###############redis Springsession####################
spring.session.store-type=redis
#spring.redis.host=localhost
#spring.redis.port=6379
spring.redis.password=123456

3.添加class文件--SpringSessionConfig.java     ###配置SpringSession所需的beans

package com.dd.common;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.http.DefaultCookieSerializer;

/**
 * Created by ckl on 2018/9/1.
 * enableRedisKeyspaceNotificationsInitializer 异常
 * spring-session中间件需要依赖redis2.8.0以上版本,并且需要开启:notify-keyspace-events
 * 如果spring-session使用的是redis集群环境,且redis集群环境没有开启Keyspace notifications功能,则应用启动时会抛出上述异常
 */
@Configuration
//maxInactiveIntervalInSeconds为SpringSession的过期时间(单位:秒)
@EnableRedisHttpSession(maxInactiveIntervalInSeconds= 1800)
public class SpringSessionConfig {
    @Bean
    public static ConfigureRedisAction configureRedisAction() {
        return ConfigureRedisAction.NO_OP;
    }

    /**
     * 配置
     * <property name="domainName" value=".dandiandenglu.com" />
     * <property name="useHttpOnlyCookie" value="true" />
     * <property name="cookiePath" value="/" />
     * <property name="cookieMaxAge" value="31536000" />
     *
     * @return
     */
    @Bean
    public static DefaultCookieSerializer defaultCookieSerializer() {
        DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
        defaultCookieSerializer.setDomainName("dandiandenglu.com"); //顶级域
        defaultCookieSerializer.setCookieName("ck");
        defaultCookieSerializer.setUseHttpOnlyCookie(true); //防止js脚本查看,xss注入
        defaultCookieSerializer.setCookiePath("/"); //根目录
        defaultCookieSerializer.setCookieMaxAge(31536000); //时间
        return defaultCookieSerializer;
    }


//    @Bean
//    public static JedisConnectionFactory jedisConnectionFactory() {
//        JedisPoolConfig config = new JedisPoolConfig();
//        config.setMaxTotal(maxTotal);
//        config.setMaxIdle(maxIdle);
//        config.setMinIdle(minIdle);
//        config.setTestOnBorrow(testOnBorrow);
//        config.setTestOnReturn(testOnReturn);
//        config.setBlockWhenExhausted(true);//连接耗尽的时候,是否阻塞,false会抛出异常,true阻塞直到超时。默认为true。
//
//        //设置redis节点
//        RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
//        RedisNode redisNode1 = new RedisNode("127.0.0.1",6379);
//        RedisNode redisNode2 = new RedisNode("127.0.0.1",6380);
//        Set<RedisNode> redisNodes = Sets.newHashSet();
//        redisNodes.add(redisNode1);
//        redisNodes.add(redisNode2);
//        redisSentinelConfiguration.setSentinels(redisNodes);
//        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisSentinelConfiguration,config);
//        jedisConnectionFactory.setPassword("123456");
//
//        return jedisConnectionFactory;
//    }

}

4.Controller控制器类

package com.dd.controller;

import com.dd.common.ResponseData;
import com.dd.model.User;
import com.dd.service.UserService;
import com.dd.util.CookieUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

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

/**
 * Created by ckl on 2018/8/31.
 */
@Slf4j
@RestController
public class LoginController {

    @Autowired
    private UserService userService;

    @RequestMapping("login")
    @ResponseBody
    public ResponseData<User> login(String username,
                                    String password,
                                    HttpSession session,
                                    HttpServletResponse httpServletResponse){
        ResponseData<User> response = userService.login(username,password);
        //登录成功
        if(response.isSuccess()){
            session.setAttribute("ckl",response.getData());
        }
        return response;
    }

    @RequestMapping("info")
    @ResponseBody
    public ResponseData<User> getInfo(HttpSession session,
                                      HttpServletRequest request){
        User user = (User) session.getAttribute("ckl");
        return ResponseData.createBySuccess(user);
    }
}

5.增加SpringSessionFilter.java    ###增加SpringSession每次访问时,重置Session的过期时间

package com.dd.common;

import com.dd.model.User;
import com.dd.util.Const;
import com.dd.util.CookieUtil;
import com.dd.util.JsonUtil;
import com.dd.util.RedisShardedPoolUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * Created by ckl on 2018/9/1.
 */
@Component
@WebFilter(urlPatterns = "/*", filterName = "springSessionFilter")
public class SpringSessionFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    /**
     * SpringSession 时间自动延长
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;

        String loginToken = CookieUtil.readLoginToken(httpServletRequest);

        if(StringUtils.isNotEmpty(loginToken)){
            //判断logintoken是否为空或者"";
            //如果不为空的话,符合条件,继续拿user信息
            String userJsonStr = RedisShardedPoolUtil.get(loginToken);
            User user = JsonUtil.string2Obj(userJsonStr,User.class);
            if(user != null){
                //如果user不为空,则重置session的时间,即调用expire命令
                RedisShardedPoolUtil.expire(loginToken, Const.RedisCacheExtime.REDIS_SESSION_EXTIME);
            }
        }
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

/**
     * 获取cookie
     * @param request
     * @return
     */
    public static String readLoginToken(HttpServletRequest request){
        Cookie[] cks = request.getCookies();
        if(cks != null){
            for (Cookie ck : cks){
                log.info("read cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
                if(StringUtils.equals(ck.getName(), COOKIE_NAME)){
                    log.info("return cookieName:{},cookieValue:{}",ck.getName(), ck.getValue());
                    return ck.getValue();
                }
            }
        }
        return  null;
    }

 /**
     * 获取
     * @param key
     * @return
     */
    public static String get(String key){
        ShardedJedis jedis = null;
        String result = null;
        try {
            jedis = RedisShardedPool.getJedis();
            result = jedis.get(key);
        } catch (Exception e) {
            log.error("get key:{} error",key,e);
            RedisShardedPool.returnBrokenResource(jedis);
            return result;
        }
        RedisShardedPool.returnResource(jedis);
        return result;
    }

 /**
     * 字符串转对象
     * @param str
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T string2Obj(String str, Class<T> clazz){
        if(StringUtils.isEmpty(str) || clazz == null)
            return null;
        try {
            return clazz.equals(String.class)? (T)str : objectMapper.readValue(str, clazz);
        }catch (Exception e) {
            log.warn("Parse String to Object error",e);
            return null;
        }
    }

6.修改本地hsot文件以便测试

路径:C:\Windows\System32\drivers\etc

# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
#      102.54.94.97     rhino.acme.com          # source server
#       38.25.63.10     x.acme.com              # x client host

# localhost name resolution is handled within DNS itself.
#	127.0.0.1       localhost
#	::1             localhost
127.0.0.1  bbb.dandiandenglu.com
127.0.0.1  aaa.dandiandenglu.com

猜你喜欢

转载自blog.csdn.net/m0_37776094/article/details/82291741