redis插件连接集群 shiro_SpringBoot+Shiro+Redis共享Session实例

点击上方“ Java研发军团 ”,选择“置顶公众号”

关键时刻,第一时间送达!

阅读本文需要5分钟

作者:小LUA

来源:http://suo.im/4wM0sx(点击阅读全文前往)

在单机版的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.0modelVersion>

    <groupId>com.examplegroupId>
    <artifactId>demoartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <packaging>jarpackaging>

    <name>demoname>
    <description>Demo project for Spring Bootdescription>

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.0.3.RELEASEversion>
        <relativePath/> 
    parent>

    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
        <java.version>1.8java.version>
    properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-thymeleafartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        
        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-allartifactId>
            <version>1.3.2version>
        dependency>
        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.47version>
        dependency>
        
        <dependency>
            <groupId>org.crazycakegroupId>
            <artifactId>shiro-redisartifactId>
            <version>3.1.0version>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <scope>runtimescope>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>3.7.0version>
                <configuration>
                    <source>1.8source>
                    <target>1.8target>
                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 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
     */@Beanpublic RedisTemplateredisTemplate(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);

    ListgetUsers();

}
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;@Overridepublic void addUser(User user) {
        user.setPassword(PasswordUtils.saltAndMd5(user.getUsername(),user.getPassword())); // 加密
        redisTemplate.boundHashOps("users").put(user.getUsername(), user);
    }@Overridepublic 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;
    }@Overridepublic ListgetUsers() {
        List list = redisTemplate.boundHashOps("users").values();
        List 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 BaseResponselogin(@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

<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>Indextitle><link th:href="@{css/index.css}" rel="stylesheet" type="text/css">head><body><div class="container"><div class="header"><h2>初级SpringBoot+Shiro小栗子 Node-Oneh2>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><script th:src="@{js/jquery-3.3.1.min.js}">script><script th:src="@{js/index.js}">script>body>html>

home.html

<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>Hometitle><link th:href="@{css/index.css}" rel="stylesheet" type="text/css">head><body><div class="container"><div class="header"><h2>初级SpringBoot+Shiro小栗子 Node-Oneh2><a href="/logout">退出登录a>div><div class="main"><table class="table"><thead><tr><th>Usernameth><th>Passwordth><th>Showth>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配置文件

ae34d7fd1ee746a53a7836c21d3825c0.png

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/

 cdc4e5461433918d456f5e51dca9b093.png

刷新看看..

f41348942c43646a6de7109f5cddcd9c.png

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

 6ea53490b5a99b798191020af91b521c.png

..

ada0eb7994a9cbfc637f5ceae9e5210c.png

github地址:https://github.com/zhiyongzhao/boot-shiro-session

END

博主还有很优秀的技术交流群,很多技术大拿,CTO,活跃度百分八十以上。问题解答百分之90以上。加博主好友后回复【加群】,然后回答技术问题,答对者才能进入,其他博主和广告商勿扰进群介绍,当然也会有一些学习资源,关注公众号后回复「获取资源」,即可免费获取「3T资源」!!

a4c26bc8e0bfd824ba41f30496c95d3e.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值