spring-boot整合spring-session,使用redis共享

本文讲述spring-boot工程中使用spring-session机制进行安全认证,并且通过redis存储session,满足集群部署、分布式系统的session共享。

java工程中,说到权限管理和安全认证,我们首先想到的是Spring Security和Apache Shiro,这两者均能实现用户身份认证和复杂的权限管理功能。但是如果我们只是想实现身份认证(如是否登录、会话是否超时),使用session管理即可满足。本文目录如下:

目录:

  1. 创建spring-boot项目

  2. 用户管理

  3. 用户身份认证

  4. spring-session配置

  5. 使用redis共享session


一、创建spring-boot项目

1、工程使用idea+gradle搭建,jdk1.8,spring-boot版本2.0.2.RELEASE,数据库postgreSQL,持久层spring-data-jpa;
2、新建spring-boot项目,工程type选择Gradle Project;
3、勾选初始化依赖如下:


初始化依赖
初始化依赖

创建完成后gradle.build文件内容如下:

buildscript {
    ext {
        springBootVersion = '2.0.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'louie.share'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile('org.springframework.boot:spring-boot-starter-web')
    runtime('org.springframework.boot:spring-boot-devtools')
    runtime('org.postgresql:postgresql')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

4、application.yml配置数据库及jpa

spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://127.0.0.1:5432/louie
    data-username: louie
    password: louie1234
  jpa:
    database: postgresql
    hibernate:
      ddl-auto: update

二、用户管理

1、创建User实体类

import org.hibernate.annotations.GenericGenerator;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;

/**
 * 用户实体
 * @author louie
 * @date created in 2018-5-12 17:28
 */
@Entity
@Table(name = "b_id_user")
public class User implements Serializable{
   @Id
    @GenericGenerator(name = "idGenerator",strategy = "uuid")
    @GeneratedValue(generator = "idGenerator")
    private String id;

    @NotBlank(message = "account can not be empty")
    private String account;
    @NotBlank(message = "password can not be empty")
    private String password;
    @NotBlank(message = "name can not be empty")
    private String name;

    //省略getter、setter
}

2、用户服务接口

import louie.share.sessionredis.bean.User;

/**
 * 用户服务接口
 * @author louie
 * @date created in 2018-5-12 17:40
 */
public interface UserService {
    /**
     * save user
     * @param user
     * @return 保存后的用户信息
     */
    User saveUser(User user);

    /**
     * find user by account
     * @param account
     * @return
     */
    User findByAccount(String account);

    /**
     * user login
     * @param account
     * @param password
     * @return
     */
    BaseResponse login(String account,String password);
}

这里省略接口的实现,您可以访问我的github和码云查看该工程的源代码(代码地址见文档底部)。

3、用户管理控制器

import louie.share.sessionredis.bean.BaseResponse;
import louie.share.sessionredis.bean.User;
import louie.share.sessionredis.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * 用户管理相关控制器
 * @author louie
 * @date created in 2018-5-12 17:26
 */

@RestController
@RequestMapping(value = "/user")
public class UserController {
    @Autowired
    private UserService userService;

    /**
     * save user
     * @param user
     * @return
     */
    @RequestMapping(value = "/save",method = RequestMethod.POST)
    public User save(User user){
        return userService.saveUser(user);
    }

    /**
     * find user by account
     * @param account
     * @return
     */
    @RequestMapping(value = "/find/{account}",method = RequestMethod.GET)
    public User find(@PathVariable String account){
        return userService.findByAccount(account);
    }

    /**
     * user login
     * @param account
     * @param password
     * @return 
     */
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    public BaseResponse login(String account,String password){
        return userService.login(account,password);
    }
}

4、创建用户
运行Application类启动服务,使用postMan访问http://localhost:8080/user/save服务创建用户:

创建用户
创建用户

三、用户身份认证

1、使用postMan访问http://localhost:8080/user/login进行用户登录校验:

微信截图_20180512184322.png-66.2kB
微信截图_20180512184322.png-66.2kB

四、spring-session配置

该部分为重点内容了,目的是实现访问资源时的安全认证、超时控制和用户登出功能。
1、修改用户登录login控制,登录成功后将用户信息写入session

/**
     * user login
     * @param account
     * @param password
     * @return
     */
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    public BaseResponse login(String account, String password,HttpSession session){
        BaseResponse response = userService.login(account,password);
        if (response.isOk()){
            session.setAttribute(session.getId(),response.getData());
        }
        return response;
    }

2、新增用户登出logout功能,将用户信息移除session

    /**
     * user logout
     * @param session
     * @return
     */
    @RequestMapping(value = "/logout")
    public String logout(HttpSession session){
        session.removeAttribute(session.getId());
        return "user logout success";
    }

3、设置session过期时间

spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://127.0.0.1:5432/louie
    data-username: louie
    password: louie1234
  jpa:
    database: postgresql
    hibernate:
      ddl-auto: update
server:
  servlet:
    session:
      timeout: "PT10M"

以下是为session有效时长为10分钟:


设置session过期时间
设置session过期时间

4、添加拦截器,通过session判断用户是否有效

import com.alibaba.fastjson.JSON;
import louie.share.sessionredis.bean.BaseResponse;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @author louie
 * @date created in 2018-5-12 19:02
 */
@Configuration
public class SessionCofig implements WebMvcConfigurer{

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SecurityInterceptor())
                //排除拦截
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/logout")

                //拦截路径
                .addPathPatterns("/**");
    }


    @Configuration
    public class SecurityInterceptor implements HandlerInterceptor{
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
            HttpSession session = request.getSession();
            if (session.getAttribute(session.getId()) != null){
                return true;
            }
            response.getWriter().write(JSON.toJSONString(
                    new BaseResponse(){{
                        setOk(false);
                        setMessage("please login first");
                    }}
            ));
            return false;
        }
    }
}

5、使用postMan访问http://localhost:8080/user/find/101,用户未登录,被拦截:

用户未登录拦截
用户未登录拦截

访问http://localhost:8080/user/login登录:

用户登录
用户登录

再次访问访问http://localhost:8080/user/find/101

登录后访问
登录后访问

五、使用redis存储session

1、添加依赖

compile('org.springframework.boot:spring-boot-starter-data-redis')
compile('org.springframework.session:spring-session-data-redis')

2、application.yml中添加配置


redis配置
redis配置

源代码:

spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://127.0.0.1:5432/louie
    data-username: louie
    password: louie1234
  jpa:
    database: postgresql
    hibernate:
      ddl-auto: update
  redis:
    database: 0
    host: localhost
    port: 6379
    password: xonro_vflow
  session:
    store-type: redis
server:
  servlet:
    session:
      timeout: "PT10M"

3、启动redis和application类,用户登录,查看redis内容:

debug查看:


debug查看session信息
debug查看session信息

redis内容:


redis内容
redis内容

工程代码已共享至github和码云,欢迎探讨学习。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容