在单机版的Springboot+Shiro的基础上,这次实现共享Session。

这里没有自己写RedisManager、SessionDAO。用的 crazycake 写的开源插件

pom.xml

<?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.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>demo</name>
<description>Demo project for Spring Boot</description> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.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>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</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>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-all -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.crazycake/shiro-redis -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.1.0</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build> </project>

redis配置文件

package com.example.demo.conf;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource; @Configuration
@PropertySource("classpath:conf/redis.properties")
public class RedisConfig { @Value("${shiro.redis.host}")
private String host; @Value("${shiro.redis.timeout}")
private int timeout; public String getHost() {
return host;
} public void setHost(String host) {
this.host = host;
} public int getTimeout() {
return timeout;
} public void setTimeout(int timeout) {
this.timeout = timeout;
}
}

Shiro配置文件

package com.example.demo.conf;

import com.example.demo.auth.PermissionRealm;
import com.example.demo.common.entity.User;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer; import java.util.LinkedHashMap; @Configuration
public class ShiroConfig { @Bean
public RedisConfig redisConfig(){
return new RedisConfig();
} @Bean
public RedisManager redisManager(){
RedisManager redisManager = new RedisManager(); // crazycake 实现
redisManager.setHost(redisConfig().getHost());
redisManager.setTimeout(redisConfig().getTimeout());
return redisManager;
} @Bean
public JavaUuidSessionIdGenerator sessionIdGenerator(){
return new JavaUuidSessionIdGenerator();
} @Bean
public RedisSessionDAO sessionDAO(){
RedisSessionDAO sessionDAO = new RedisSessionDAO(); // crazycake 实现
sessionDAO.setRedisManager(redisManager());
sessionDAO.setSessionIdGenerator(sessionIdGenerator()); // Session ID 生成器
return sessionDAO;
} @Bean
public SimpleCookie cookie(){
SimpleCookie cookie = new SimpleCookie("SHAREJSESSIONID"); // cookie的name,对应的默认是 JSESSIONID
cookie.setHttpOnly(true);
cookie.setPath("/"); // path为 / 用于多个系统共享JSESSIONID
return cookie;
} @Bean
public DefaultWebSessionManager sessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setGlobalSessionTimeout(redisConfig().getTimeout()); // 设置session超时
sessionManager.setDeleteInvalidSessions(true); // 删除无效session
sessionManager.setSessionIdCookie(cookie()); // 设置JSESSIONID
sessionManager.setSessionDAO(sessionDAO()); // 设置sessionDAO
return sessionManager;
} /**
* 1. 配置SecurityManager
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm()); // 设置realm
securityManager.setSessionManager(sessionManager()); // 设置sessionManager
// securityManager.setCacheManager(redisCacheManager()); // 配置缓存的话,退出登录的时候crazycake会报错,要求放在session里面的实体类必须有个id标识
return securityManager;
} /**
* 2. 配置缓存
* @return
*/
// @Bean
// public CacheManager cacheManager(){
// EhCacheManager ehCacheManager = new EhCacheManager();
// ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
// return ehCacheManager;
// } @Bean
public RedisCacheManager redisCacheManager(){
RedisCacheManager cacheManager = new RedisCacheManager(); // crazycake 实现
cacheManager.setRedisManager(redisManager());
return cacheManager;
} /**
* 3. 配置Realm
* @return
*/
@Bean
public AuthorizingRealm realm(){
PermissionRealm realm = new PermissionRealm();
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
// 指定加密算法
matcher.setHashAlgorithmName("MD5");
// 指定加密次数
matcher.setHashIterations(10);
// 指定这个就不会报错
matcher.setStoredCredentialsHexEncoded(true);
realm.setCredentialsMatcher(matcher);
return realm;
} /**
* 4. 配置LifecycleBeanPostProcessor,可以来自动的调用配置在Spring IOC容器中 Shiro Bean 的生命周期方法
* @return
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
} /**
* 5. 启用IOC容器中使用Shiro的注解,但是必须配置第四步才可以使用
* @return
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
return new DefaultAdvisorAutoProxyCreator();
} /**
* 6. 配置ShiroFilter
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(){
LinkedHashMap<String, String> map = new LinkedHashMap<>();
// 静态资源
map.put("/css/**", "anon");
map.put("/js/**", "anon"); // 公共路径
map.put("/login", "anon");
map.put("/register", "anon");
//map.put("/*", "anon"); // 登出,项目中没有/logout路径,因为shiro是过滤器,而SpringMVC是Servlet,Shiro会先执行
map.put("/logout", "logout"); // 授权
map.put("/user/**", "authc,roles[user]");
map.put("/admin/**", "authc,roles[admin]"); // everything else requires authentication:
map.put("/**", "authc"); ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 配置SecurityManager
factoryBean.setSecurityManager(securityManager());
// 配置权限路径
factoryBean.setFilterChainDefinitionMap(map);
// 配置登录url
factoryBean.setLoginUrl("/");
// 配置无权限路径
factoryBean.setUnauthorizedUrl("/unauthorized");
return factoryBean;
} /**
* 配置RedisTemplate,充当数据库服务
* @return
*/
@Bean
public RedisTemplate<String,User> redisTemplate(RedisConnectionFactory connectionFactory){
RedisTemplate<String,User> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<User>(User.class));
return redisTemplate;
} }

UserService

package com.example.demo.service;

import com.example.demo.common.entity.User;

import java.util.List;

public interface UserService {

    void addUser(User user);

    User login(User user);

    List<User> getUsers();

}

impl

package com.example.demo.service.impl;

import com.example.demo.common.PasswordUtils;
import com.example.demo.common.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import java.util.ArrayList;
import java.util.List; @Service
public class UserServiceImpl implements UserService { @Autowired
private RedisTemplate<String, User> redisTemplate; @Override
public void addUser(User user) {
user.setPassword(PasswordUtils.saltAndMd5(user.getUsername(),user.getPassword())); // 加密
redisTemplate.boundHashOps("users").put(user.getUsername(), user);
} @Override
public User login(User user) {
user.setPassword(PasswordUtils.saltAndMd5(user.getUsername(),user.getPassword())); // 加密
User u = (User) redisTemplate.boundHashOps("users").get(user.getUsername());
if (u == null || !check(user, u)){
return null;
}
return u;
} @Override
public List<User> getUsers() {
List<Object> list = redisTemplate.boundHashOps("users").values();
List<User> users = new ArrayList<>();
list.forEach(u->{
users.add((User) u);
});
return users;
} private boolean check(User a, User b){
if (a.getUsername().equals(b.getUsername()) && a.getPassword().equals(b.getPassword())){
return true;
}
return false;
}
}

controller

package com.example.demo.controller;

import com.example.demo.common.entity.User;
import com.example.demo.common.response.BaseResponse;
import com.example.demo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView; @RestController
public class SimpleController { @Autowired
private UserService userService; @RequestMapping("/")
public ModelAndView index(){
return new ModelAndView("index");
} @RequestMapping("/login")
public BaseResponse<String> login(@RequestBody User user){
BaseResponse<String> response = new BaseResponse<>(0,"登陆成功");
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(
user.getUsername(), user.getPassword());
subject.login(token);
response.setData("/home");
return response;
} @RequestMapping("/register")
public BaseResponse register(@RequestBody User user){
userService.addUser(user);
return new BaseResponse(0,"注册成功");
} @RequestMapping("/home")
public ModelAndView home(){
ModelAndView mv = new ModelAndView("home");
mv.addObject("users", userService.getUsers());
return mv;
}
}

redis.properties

shiro.redis.host=localhost:6379
shiro.redis.timeout=1800000

applicatin.properties

#server.port=8080
server.port=8081
#server.port=8082 spring.redis.host=127.0.0.1
spring.redis.port=6379

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Index</title>
<link th:href="@{css/index.css}" rel="stylesheet" type="text/css">
</head>
<body>
<div class="container">
<div class="header">
<h2>初级SpringBoot+Shiro小栗子 Node-One</h2>
<!--<h2>初级SpringBoot+Shiro小栗子 Node-Two</h2>-->
</div>
<div class="main">
<div class="left">
<div class="form-group">
<input type="text" name="username" placeholder="请输入用户名">
</div>
<div class="form-group">
<input type="password" name="password" placeholder="请输入密码">
</div>
<div class="form-group">
<a href="javascript:;" id="login">登录</a>
</div>
<div class="form-group">
<a href="/home">点我!不登录进不去</a>
</div>
</div>
<div class="right">
<div class="form-group">
<input type="text" name="username" placeholder="请输入用户名">
</div>
<div class="form-group">
<input type="password" name="password" placeholder="请输入密码">
</div>
<div class="form-group">
<input type="text" name="show" placeholder="自我介绍">
</div>
<div class="form-group">
<a href="javascript:;" id="register">注册</a>
</div>
</div>
</div>
</div>
<!--<div class="tip-wrap">-->
<!--<div class="tip">似懂非懂</div>-->
<!--</div>-->
<script th:src="@{js/jquery-3.3.1.min.js}"></script>
<script th:src="@{js/index.js}"></script>
</body>
</html>

home.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Home</title>
<link th:href="@{css/index.css}" rel="stylesheet" type="text/css">
</head>
<body>
<div class="container">
<div class="header">
<h2>初级SpringBoot+Shiro小栗子 Node-One</h2>
<!--<h2>初级SpringBoot+Shiro小栗子 Node-Two</h2>-->
<a href="/logout">退出登录</a>
</div>
<div class="main">
<table class="table">
<thead>
<tr>
<th>Username</th>
<th>Password</th>
<th>Show</th>
</tr>
</thead>
<tbody>
<tr th:each="u : ${users}">
<td>[[${u.username}]]</td>
<td>[[${u.password}]]</td>
<td>[[${u.show}]]</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>

以上两种配置各打包一次(记得留着打包好的jar包)

下载Nginx

解压到无中文目录,修改Nginx配置文件

    upstream myapp{
server 127.0.0.1:8081 weight=1;
server 127.0.0.1:8082 weight=1;
} server{
listen 80;
server_name myapp; location / {
proxy_pass http://myapp;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

到此,先启动两个jar包(分别是8081,Node-One;8082,Node-Two)

然后启动Nginx

浏览器访问:http://localhost/

刷新看看..

..

随便在一个节点上注册,登录,然后刷新到另外一个节点,发现不用登录就可以访问权限资源

..

GitHub

SpringBoot+Shiro+Redis共享Session入门小栗子的更多相关文章

  1. SpringBoot,Security4, redis共享session,分布式SESSION并发控制,同账号只能登录一次

    由于集成了spring session ,redis 共享session,导致SpringSecurity单节点的session并发控制失效, springSession 号称 无缝整合httpses ...

  2. SpringBoot系列: Redis 共享Session

    Web项目Session管理是一个很重要的话题, 涉及到系统横向扩展, SpringBoot已经为共享Session很好的解决方案, 这篇文章关注使用Redis共享会话, 同时这也是最常用的方法. = ...

  3. SpringBoot SpringSession redis 共享 SESSION

    号称无缝整合httpsession 共享, 但注意如果存在第三方框架,例如SESSION并发控制,这个是需要自己重写session名单的. 关于redis session 共享 的session并发控 ...

  4. SpringBoot+Shiro入门小栗子

    写一个不花里胡哨的纯粹的Springboot+Shiro的入门小栗子 效果如图: 首页:有登录注册 先注册一个,然后登陆 登录,成功自动跳转到home页 home页:通过认证之后才可以进 代码部分: ...

  5. springboot+shiro+redis(单机redis版)整合教程-续(添加动态角色权限控制)

    相关教程: 1. springboot+shiro整合教程 2. springboot+shiro+redis(单机redis版)整合教程 3. springboot+shiro+redis(集群re ...

  6. springboot+shiro+redis(集群redis版)整合教程

    相关教程: 1. springboot+shiro整合教程 2. springboot+shiro+redis(单机redis版)整合教程 3.springboot+shiro+redis(单机red ...

  7. springboot+shiro+redis(单机redis版)整合教程

    相关教程: 1. springboot+shiro整合教程 2. springboot+shiro+redis(集群redis版)整合教程 3.springboot+shiro+redis(单机red ...

  8. Tomcat7.0.99集群使用Redis共享session方案

    以前配置过给予多播的session共享方案,这回再配置一个redis共享session的. 先小小的炫耀一下: 相信大家要做Tomcat+Redis+session配置,遇到的头号麻烦就是编译的tom ...

  9. linux下实现redis共享session的tomcat集群

    为了实现主域名与子域名的下不同的产品间一次登录,到处访问的效果,因此采用rediss实现tomcat的集群效果.基于redis能够异步讲缓存内容固化到磁盘上,从而当服务器意外重启后,仍然能够让sess ...

随机推荐

  1. Prism框架研究(三)

    这一篇主要用来介绍一下基于Prism Library中的核心服务以及如何配置Container,还有一个重要的部分是如何管理各个组件之间的依赖性,下面就这些内容来做一一的介绍. 1 Prism中的核心 ...

  2. react & youtube

    react & youtube https://www.npmjs.com/package/react-youtube https://developers.google.com/youtub ...

  3. 如何快速定位到DBGrid的某一行!!!急...

    比如我查找张三,那么DBGrid就可以定位到张三那行并选中这行,除了用循环实现还有没有快速定位的方法,谢谢! 解决方案 » to SuperTitan001 那如何找到张三的这行呢?除了用循环还有什么 ...

  4. oracle数据库备份和恢复

    参考地址:https://www.cnblogs.com/1175429393wljblog/p/9529334.html Oracle数据导入导出imp/exp 在cmd的dos命令提示符下执行,而 ...

  5. MySQL 优化小技巧

    碎片整理: mysql数据一开始是在磁盘上顺序存放的,如果数据表有频繁的update改动,那么数据就会形成很多碎片,拖慢速度和不利于索引: 优化碎片有两种方式: alter table user en ...

  6. JSON in SQL Server 2016

    JSON functions in SQL Server enable you to analyze and query JSON data, transform JSON to relational ...

  7. hibernate多对多映射文件的配置

    user.hbm.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate ...

  8. Spring 使用介绍(三)—— 资源

    一.Resource接口 Spring提供Resource接口,代表底层外部资源,提供对底层外部资源的一致性访问接口 public interface InputStreamSource { Inpu ...

  9. Git——简说.git目录【五】

    我们都知道初始化项目时,会生成一个.git的隐藏目录,那么这个目录究竟放了那些东西呢,又有什么作用呢?现在我们就来看看. HEAD HEAD指向的是当前工作所在的分支 $ cat HEAD ref: ...

  10. Python中matplotlib模块解析

    用Matplotlib绘制二维图像的最简单方法是: 1.  导入模块 导入matplotlib的子模块 import matplotlib.pyplot as plt import numpy as ...