Nginx shiro redis 多tomcat共享session

pom.xml:

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.sheng.webapp</groupId>
  <artifactId>redis_session</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>redis_session Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.version>4.3.9.RELEASE</spring.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
      <version>1.1.1.RELEASE</version>
      <type>pom</type>
    </dependency>
    <!--Spring-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
      <scope>test</scope>
    </dependency>
    <!---shiro相关-->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>1.2.3</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-web</artifactId>
      <version>1.2.3</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.2.3</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-ehcache</artifactId>
      <version>1.2.3</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
      <type>jar</type>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.25</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>redis_session</finalName>
  </build>
</project>
applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.sheng.example">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>
</beans>
applicationContext-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- 配置緩存管理器 -->
	<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
		<!-- 指定 ehcache 的配置文件 -->
		<property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml" />
	</bean>

	<!-- 配置进行授权和认证的 Realm -->
	<bean id="myRealm" class="com.sheng.example.MyRealm" init-method="setCredentialMatcher"></bean>
	<!-- 配置 Shiro 的 SecurityManager Bean. -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="sessionManager" ref="sessionManager" />
		<property name="cacheManager" ref="cacheManager" />
		<property name="realm" ref="myRealm" />
	</bean>

	<!-- 配置 Bean 后置处理器: 会自动的调用和 Spring 整合后各个组件的生命周期方法. -->
	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

	<!-- 配置 ShiroFilter bean: 该 bean 的 id 必须和 web.xml 文件中配置的 shiro filter 的 
		name 一致 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!-- 装配 securityManager -->
		<property name="securityManager" ref="securityManager" />
		<!-- 配置登陆页面 -->
		<property name="loginUrl" value="/login.jsp" />
		<!-- 登陆成功后的一面 -->
		<property name="successUrl" value="/shiro-success.jsp" />
		<property name="unauthorizedUrl" value="/shiro-unauthorized.jsp" />
		<!-- 具体配置需要拦截哪些 URL, 以及访问对应的 URL 时使用 Shiro 的什么 Filter 进行拦截. -->
		<property name="filterChainDefinitions">
			<value>
				<!--静态资源-->
				/css/**  = anon
				/images/**  = anon
				/assets/**=anon
				/js/**=anon
				<!-- 配置登出: 使用 logout 过滤器 -->
				/=anon
				/index.jsp=anon
				/login=anon
				/user.jsp = roles[user]
				/admin.jsp = roles[admin]
				/** = authc
			</value>
		</property>

		<property name="filters">
			<map>
				<entry key="authc">
					<bean class="com.sheng.example.AuthcLevelFilter"></bean>
				</entry>
				<!-- <entry key="roles"> <bean class="com.atguigu.shiro.realm.RolesAnyAuthorizationFilter"></bean> 
					</entry> -->
			</map>
		</property>

	</bean>
	<!-- 会话管理器 -->
	<bean id="sessionManager"
		class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
		<property name="globalSessionTimeout" value="60000" />
		<property name="deleteInvalidSessions" value="true" />
		<property name="sessionValidationSchedulerEnabled" value="true" /><!-- 定时检查失效的session -->
		<!-- <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/> -->
		<property name="sessionDAO" ref="sessionDAO" />
		<!--<property name="sessionIdCookie.name" value="jsid"/>  -->
		<!--<property name="sessionIdCookieEnabled" value="true" />-->
		<!--<property name="sessionIdCookie" ref="sessionIdCookie" />-->
	</bean>
	<!-- 会话验证调度器 -->
	<!--<bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler"> 
		<property name="sessionValidationInterval" value="1800000"/> <property name="sessionManager" 
		ref="sessionManager"/> </bean> -->
	<!-- 会话DAO -->
	<bean id="sessionDAO" class="com.sheng.example.RedisSessionDao"></bean>
	<!--<bean id="sessionDAO"-->
		<!--class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">-->
		<!--<property name="activeSessionsCacheName" value="shiro-activeSessionCache" />-->
		<!--<property name="sessionIdGenerator" ref="sessionIdGenerator" />-->
	<!--</bean>-->
	<!-- 会话ID生成器 -->
	<bean id="sessionIdGenerator"
		class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" />
	<!-- 会话Cookie模板 -->
	<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
		<constructor-arg value="sid" />
		<property name="httpOnly" value="true" />
		<property name="maxAge" value="86400" />
		<property name="name" value="daskldj"></property>
		<property name="domain" value="sadf"></property>
	</bean>

</beans>

ehcache-shiro.xml

<!--
  ~ Licensed to the Apache Software Foundation (ASF) under one
  ~ or more contributor license agreements.  See the NOTICE file
  ~ distributed with this work for additional information
  ~ regarding copyright ownership.  The ASF licenses this file
  ~ to you under the Apache License, Version 2.0 (the
  ~ "License"); you may not use this file except in compliance
  ~ with the License.  You may obtain a copy of the License at
  ~
  ~     http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing,
  ~ software distributed under the License is distributed on an
  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  ~ KIND, either express or implied.  See the License for the
  ~ specific language governing permissions and limitations
  ~ under the License.
  -->

<!-- EhCache XML configuration file used for Shiro spring sample application -->
<ehcache>

    <!-- Sets the path to the directory where cache .data files are created.

If the path is a Java System Property it is replaced by
its value in the running VM.

The following properties are translated:
user.home - User's home directory
user.dir - User's current working directory
java.io.tmpdir - Default temp file path -->
    <diskStore path="java.io.tmpdir/shiro-spring-sample"/>


    <!--Default Cache configuration. These will applied to caches programmatically created through
    the CacheManager.

    The following attributes are required:

    maxElementsInMemory            - Sets the maximum number of objects that will be created in memory
    eternal                        - Sets whether elements are eternal. If eternal,  timeouts are ignored and the
                                     element is never expired.
    overflowToDisk                 - Sets whether elements can overflow to disk when the in-memory cache
                                     has reached the maxInMemory limit.

    The following attributes are optional:
    timeToIdleSeconds              - Sets the time to idle for an element before it expires.
                                     i.e. The maximum amount of time between accesses before an element expires
                                     Is only used if the element is not eternal.
                                     Optional attribute. A value of 0 means that an Element can idle for infinity.
                                     The default value is 0.
    timeToLiveSeconds              - Sets the time to live for an element before it expires.
                                     i.e. The maximum time between creation time and when an element expires.
                                     Is only used if the element is not eternal.
                                     Optional attribute. A value of 0 means that and Element can live for infinity.
                                     The default value is 0.
    diskPersistent                 - Whether the disk store persists between restarts of the Virtual Machine.
                                     The default value is false.
    diskExpiryThreadIntervalSeconds- The number of seconds between runs of the disk expiry thread. The default value
                                     is 120 seconds.
    memoryStoreEvictionPolicy      - Policy would be enforced upon reaching the maxElementsInMemory limit. Default
                                     policy is Least Recently Used (specified as LRU). Other policies available -
                                     First In First Out (specified as FIFO) and Less Frequently Used
                                     (specified as LFU)
    -->

    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            />

    <!-- We want eternal="true" (with no timeToIdle or timeToLive settings) because Shiro manages session
expirations explicitly.  If we set it to false and then set corresponding timeToIdle and timeToLive properties,
ehcache would evict sessions without Shiro's knowledge, which would cause many problems
(e.g. "My Shiro session timeout is 30 minutes - why isn't a session available after 2 minutes?"
Answer - ehcache expired it due to the timeToIdle property set to 120 seconds.)

diskPersistent=true since we want an enterprise session management feature - ability to use sessions after
even after a JVM restart.  -->
    <cache name="shiro-activeSessionCache"
           maxElementsInMemory="10000"
           eternal="true"
           overflowToDisk="true"
           diskPersistent="true"
           diskExpiryThreadIntervalSeconds="600"/>

    <cache name="org.apache.shiro.realm.SimpleAccountRealm.authorization"
           maxElementsInMemory="100"
           eternal="false"
           timeToLiveSeconds="600"
           overflowToDisk="false"/>

</ehcache>


springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 配置自动扫描的包 -->
    <context:component-scan base-package="com.sheng.example" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>
    <!--一下内容只能放在springmvc.xml中  不可放在ApplicationContext.xml或ApplicationContext-shiro.xml中-->
    <!--<bean id="exceptionHandler" class="com.sheng.atx.parking.handlers.MyExceptionHandler"/>-->
    <!-- 使 Shiro 的注解起作用, Shiro 的注解标示在方法上. 例如 @RequiresRoles、@RequiresPermissions -->
    <!-- 因为目前是在 Handler 的方法上添加注解, 所以以下的配置需要作用在 SpringMVC 的 IOC 容器中. 而不是其父容器中.  -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>
</beans>
web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd">
  <!-- 指定Spring Bean的配置文件所在目录。默认配置在WEB-INF目录下 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext*.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <!-- 配置  Shiro 的 Filter -->
  <filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <async-supported>true</async-supported>
    <init-param>
      <param-name>targetFilterLifecycle</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

AuthcLevelFilter.java

package com.sheng.example;
import org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class AuthcLevelFilter extends PassThruAuthenticationFilter {
	@SuppressWarnings("deprecation")
	@Override
	    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
	    HttpServletResponse httpResponse ;
	    HttpServletRequest httpRequest;
	    Map<String, Object> map = new HashMap<String, Object>();
	    httpResponse = WebUtils.toHttp(response); 
	    httpRequest = WebUtils.toHttp(request);

		if(isLoginRequest(httpRequest, httpResponse)){
	    return true;
	    }else {

			WebUtils.saveRequest(request);
			String url = httpRequest.getRequestURI();
			map.put("requestUri", url);
			PrintWriter out=null;
			try {
				httpResponse.setContentType("text/html; charset=utf-8");
				out =  httpResponse.getWriter();
//				if (url.equals("/logList.jsp")||url.equals("/parkManager.jsp")) {
//					httpResponse.setContentType("text/html;charset=utf-8");
//					out.append("<script language='javascript'>parent.location='../index.jsp';</script>");
//				}else if(url.equals("/getParkList")||url.equals("/getLogList_admin")||url.equals("/getLogList_park")){
//					PageData pageData=new PageData();
//					pageData.setData(new ArrayList<>());
//					ReturnData ret = new ReturnData();
//					ret.setMessage(URLEncoder.encode("用户未登录,试图访问!","utf-8"));
//					pageData.setOtherData(ret);
//					httpResponse.setCharacterEncoding("UTF-8");
//					httpResponse.setContentType("application/json; charset=utf-8");
//					out.append(JSONObject.toJSONString(pageData));
//				} else if(url.equals("/park/query_coast")) {
//					ReturnData ret = new ReturnData();
//					ret.setStatus(false);
//					ret.setMessage(URLEncoder.encode("用户未登录,试图访问!","utf-8"));
//					out.append(JSONObject.toJSONString(ret));
//				}else{
//					System.out.println("requestUri:" + httpRequest.getRequestURI());
//					httpResponse.setStatus(200, "redirect");
//					httpResponse.setCharacterEncoding("UTF-8");
//					httpResponse.setContentType("application/json; charset=utf-8");
//
//						ReturnData ret = new ReturnData();
//						ret.setMessage("用户未登录,试图访问!");
//						ret.setData(httpResponse.toString());
						out.append("用户未登录,试图访问!");
//
//
//				}
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				if (out != null) {
					out.close();
				}
			}
				return false;

			}
	    }
	
}
MyController.java

package com.sheng.example;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

/**
 * Created by Administrator on 2017/6/30 0030.
 */
@Controller
public class MyController {

    @RequestMapping("test")
    @ResponseBody
    public String index(){
        Subject currentUser = SecurityUtils.getSubject();
        return "Hello:"+currentUser.getPrincipal().toString()+",我是tomcat2";
    }
    @RequestMapping("login")
    public ModelAndView login(String username,String password){
        ModelAndView modelAndView=null;
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            subject.login(token);
            modelAndView=new ModelAndView("test");
            modelAndView.addObject("username",username);
        }catch (AuthenticationException ae){
            modelAndView=new ModelAndView("/");
            modelAndView.addObject("message","用户名或密码错误");
        }

        return modelAndView;
    }
}
MyRealm.java

package com.sheng.example;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

/**
 * Created by Administrator on 2017/6/30 0030.
 */
public class MyRealm extends AuthorizingRealm {
    /**
     * 授权方法:
     * 1. 实际返回的是 SimpleAuthorizationInfo 类的实例
     * 2. 可以调用 SimpleAuthorizationInfo 的 addRole 来添加当前登录 user 的权限信息.
     * 3. 可以调用 PrincipalCollection 参数的 getPrimaryPrincipal() 方法来获取用户信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        Object principal = principalCollection.getPrimaryPrincipal();
//        User user = userService.getAdmin(principal.toString());
//		if("admin".equals(principal)){
//        info.addRole(user.getRole().getName());
//			System.out.println("用户角色admin");
//			info.addRole("list");
//		}
//		if("user".equals(principal)){
//			info.addRole("list");
//		}

//		info.addRole("user");

        return info;
    }

    /**
     * 认证方法
     * 1. 编写表单: 表单的 action、和 username、password 的参数都是什么 ?
     * 回答: 提交到你想提交的地方, username 和 password 也参数名称都任意.
     * 2. 例如, 提交到了一个 SpringMVC 的 handler:
     * 1). 获取用户名、密码
     * 2).
     * Subject currentUser = SecurityUtils.getSubject();
     * UsernamePasswordToken token = new UsernamePasswordToken(username, password);
     * currentUser.login(token);
     * 3. 当 Subject 调用 login 方法时, 即会触发当前的 doGetAuthenticationInfo 方法. 且把
     * UsernamePasswordToken 对象传入, 然后再该方法中执行真正的认证: 访问数据库进行比对.
     * 1). 获取用户名和密码
     * 2).
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        System.out.println("doGetAuthenticationInfo------->"+token.getPrincipal());
        System.out.println("doGetAuthenticationInfo------->"+token.getCredentials());

        //1. 从 token 中获取登录的 username! 注意不需要获取 password.

        //2. 利用 username 查询数据库得到用户的信息.

        //3. 创建 SimpleAuthenticationInfo 对象并返回. 注意该对象的凭证式从数据库中查询得到的.
        //而不是页面输入的. 实际的密码校验可以交由 Shiro 来完成

        //4. 关于密码加密的事: shiro 的密码加密可以非常非常的复杂, 但实现起来却可以非常简单.
        //1). 可以选择加密方式: 在当前的 realm 中编写一个 public 类型的不带参数的方法, 使用 @PostConstruct
        //注解进行修饰, 在其中来设置密码的匹配方式.
        //2). 设置盐值: 盐值一般是从数据库中查询得到的.
        //3). 调用 new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName)
        //构造器返回 SimpleAuthenticationInfo 对象:  credentialsSalt 为
        //ByteSource credentialsSalt = new Md5Hash(source);

        //登陆的主要信息: 可以是一个实体类的对象, 但该实体类的对象一定是根据 token 的 username 查询得到的.
        Object principal = token.getPrincipal();//获取登录名
        //认证信息: 从数据库中查询出来的信息. 密码的比对交给 shiro 去进行比较
        String credentials = "789ecdde95405b37ffafd9c4e460b4a9";
        //设置盐值:
        String source = "abcdefg";
        ByteSource credentialsSalt = new Md5Hash(source);
//		System.out.println(credentialsSalt);
//        User atx_admin=null;
//        try {
//            atx_admin = userService.getAdmin(token.getPrincipal().toString());
//            if(atx_admin.getRole().getName().equals("park_station")){//数据库不加密前台人员密码
//                atx_admin.setPassword(strToMD5(atx_admin.getPassword()));
//            }
			System.out.println("数据库密码:" + atx_admin.getPassword());
//        }catch (Exception e){
//            e.printStackTrace();
//        }
//        if(atx_admin==null)
//            throw new AuthenticationException("用户名不存在");
        //当前 Realm 的 name
        String realmName = getName();
//		System.out.println("realmName:"+realmName);
        SimpleAuthenticationInfo info =
                new SimpleAuthenticationInfo(principal, credentials,
                        credentialsSalt, realmName);

        return info;
    }

    //@PostConstruct: 相当于 bean 节点的 init-method 配置.
    public void setCredentialMatcher(){
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();

        credentialsMatcher.setHashAlgorithmName("MD5");
        credentialsMatcher.setHashIterations(1024);

        setCredentialsMatcher(credentialsMatcher);
    }
    public static String strToMD5(String str){
        String saltSource = "abcdefg";

        String hashAlgorithmName = "MD5";//加密方式
        Object salt = new Md5Hash(saltSource);//盐值
        int hashIterations = 1024;//加密次数

        //加密后的密码
        Object result = new SimpleHash(hashAlgorithmName, str, salt, hashIterations);
        return result.toString();
    }
    public static void main(String[] args) {
        String saltSource = "abcdefg";

        String hashAlgorithmName = "MD5";//加密方式
        String credentials = "123";//密码
        Object salt = new Md5Hash(saltSource);//盐值
        int hashIterations = 1024;//加密次数

        //加密后的密码
        Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
        System.out.println(result);
        System.out.println("68f3139a38b232392cc9d3b6ddd762f7".equals(result.toString()));
    }
}
RedisClient.java

package com.sheng.example;

import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.Session;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.io.*;

/**
 * Created by Administrator on 2017/6/30 0030.
 */
public class RedisClient {
    private static JedisPool pool;
    private static String redisServerIp="192.168.31.7";

    /**
     * 建立连接池 真实环境,一般把配置参数缺抽取出来。
     *
     */
    private static void createJedisPool() {

        // 建立连接池配置参数
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxWaitMillis(1000);
        // 设置最大连接数
//        config.setMaxActive(1000);

        // 设置最大阻塞时间,记住是毫秒数milliseconds
//        config.setMaxWait(1000);
        // 设置空间连接
        config.setMaxIdle(10);
        config.setMaxTotal(100);
        config.setMinIdle(2);
        // 创建连接池
        pool = new JedisPool(config, redisServerIp, 6379);

    }

    /**
     * 在多线程环境同步初始化
     */
    private static synchronized void poolInit() {
        if (pool == null)
            createJedisPool();
    }

    /**
     * 获取一个jedis 对象
     *
     * @return
     */
    private static Jedis getJedis() {

        if (pool == null)
            poolInit();
        return pool.getResource();
    }

    /**
     * 归还一个连接
     *
     * @param jedis
     */
    private static void returnRes(Jedis jedis) {
        pool.returnResource(jedis);
    }

    void set(String sessionId, Session session) {
        jedis = getJedis();
        jedis.append(sessionId, serialize(session));
        returnRes(jedis);
    }

    void replace(String sessionId, Session session) {
        set(sessionId, session);

    }

    Jedis jedis = null;

    void delete(String sessionId) {
        jedis = getJedis();
        jedis.del(sessionId);
        returnRes(jedis);
    }

    Object get(String sessionId) {
        jedis = getJedis();
        Object obj = deserialize(jedis.get(sessionId));
        returnRes(jedis);
        return obj;
    }

    private static Object deserialize(String str) {
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            bis = new ByteArrayInputStream(Base64.decode(str));
            ois = new ObjectInputStream(bis);
            return ois.readObject();
        } catch (Exception e) {
            throw new RuntimeException("deserialize session error", e);
        } finally {
            try {
                ois.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    private static String serialize(Object obj) {
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        try {
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            return Base64.encodeToString(bos.toByteArray());
        } catch (Exception e) {
            throw new RuntimeException("serialize session error", e);
        } finally {
            try {
                oos.close();
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
    public static void main(String[] args){
        Jedis jedisCli = new Jedis("192.168.31.7", 6379); //新建Jedis对象
        jedisCli.select(2); //切换Redis数据库
        jedisCli.set("firstJedis", "hello,Jedis"); //与Redis命令行操作基本一致
    }
}

RedisSessionDao.java

package com.sheng.example;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;

/**
 * Created by Administrator on 2017/6/30 0030.
 */
public class RedisSessionDao extends AbstractSessionDAO {
    private RedisClient sessionCacheClient=new RedisClient();

    Logger log= LoggerFactory.getLogger(getClass());

    public void update(Session session) throws UnknownSessionException {
        log.info("更新seesion,id=[{}]",session.getId().toString());
        try {
            sessionCacheClient.replace(session.getId().toString(), session);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void delete(Session session) {
        log.info("删除seesion,id=[{}]",session.getId().toString());
        try {
            sessionCacheClient.delete(session.getId().toString());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public Collection<Session> getActiveSessions() {
        System.out.println("getActiveSessions");
        log.info("获取存活的session");
        return Collections.emptySet();
    }

    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = generateSessionId(session);
        assignSessionId(session, sessionId);
        log.info("创建seesion,id=[{}]",session.getId().toString());
        try {
            sessionCacheClient.set(sessionId.toString(),  session);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        log.info("获取seesion,id=[{}]",sessionId.toString());
        Session session = null;
        try {
            session = (Session) sessionCacheClient.get(sessionId.toString());
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return session;
    }
}

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<body>
<h2>Hello World!</h2>
<form action="login">
    <input type="text" name="username">
    <input type="password" name="password">
    <input type="submit" value="提交">
</form>
<%=session.getId()%>
<script>
    <c:if test="${not empty message}">
        alert("${message}");
    </c:if>
</script>
</body>
</html>


打包后放在不同tomcat下 即可实现session共享




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

什么都搞点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值