SpringBoot+Shiro+Redis共享Session入门
共 15443字,需浏览 31分钟
·
2020-12-04 15:48
点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
作者 | 苏先生139
来源 | urlify.cn/V7Jbeu
在单机版的Springboot+Shiro的基础上,这次实现共享Session。这里没有自己写RedisManager、SessionDAO。用的 crazycake 写的开源插件
pom.xml
"1.0" encoding="UTF-8"?>
"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">
4.0.0
com.example
demo
0.0.1-SNAPSHOT
jar
demo
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
2.0.3.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
org.apache.shiro
shiro-all
1.3.2
com.alibaba
fastjson
1.2.47
org.crazycake
shiro-redis
3.1.0
org.springframework.boot
spring-boot-devtools
runtime
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
org.apache.maven.plugins
maven-compiler-plugin
3.7.0
<source>1.8source>
1.8
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 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 redisTemplate(RedisConnectionFactory connectionFactory){
RedisTemplate redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(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 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 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 getUsers() {
List
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 login(@RequestBody User user){
BaseResponse 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
"en" xmlns:th="http://www.thymeleaf.org">
"UTF-8">
Index
"@{css/index.css}" rel="stylesheet" type="text/css">
"container">
"header">
初级SpringBoot+Shiro小栗子 Node-One
"main">
"left">
"form-group">
type="text" name="username" placeholder="请输入用户名">
"form-group">
type="password" name="password" placeholder="请输入密码">
"right">
"form-group">
type="text" name="username" placeholder="请输入用户名">
"form-group">
type="password" name="password" placeholder="请输入密码">
"form-group">
type="text" name="show" placeholder="自我介绍">
home.html
"en" xmlns:th="http://www.thymeleaf.org">
"UTF-8">
Home
"@{css/index.css}" rel="stylesheet" type="text/css">
"container">
"main">
"table">
Username
Password
Show
"u : ${users}">
[[${u.username}]]
[[${u.password}]]
[[${u.show}]]
以上两种配置各打包一次(记得留着打包好的jar包)
http://nginx.org/
解压到无中文目录,修改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
https://github.com/Mysakura/boot-shiro-session
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
BaseResponse
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;
}
}
粉丝福利:实战springboot+CAS单点登录系统视频教程免费领取
???
?长按上方微信二维码 2 秒 即可获取资料
感谢点赞支持下哈