[TOC]
公司随着业务的增长,各业务进行水平扩展面临拆分;随着业务的拆分各种管理系统扑面而来,为了方便权限统一管理,不得不自己开发或使用分布式权限管理(Spring Security)。Spring Security依赖Spring和初级开发人员学习难度大,中小型公司不推荐使用;Apache Shiro是一个强大易用的安全框架,Shiro的API方便理解。经过网上各路大神对shiro与spring security的比较,最终决定使用shiro开发一个独立的权限管理平台。
该项目是在张开涛跟我学shiro Demo基础上进行开发、功能完善和管理页面优化,欢迎fork、star及提出改进意见。
<iframe height=500 width=100% src="http://yzfile.oss-cn-beijing.aliyuncs.com/QQ20180818-212046-HD.mp4"></iframe>通过注解获取当前登录用户
拦截所有请求 1.通过shiro Subject 获取当前用户的用户名 2.通过用户名获取用户信息 3.将用户信息保存ServletRequest对象中
通过SpringAOP拦截容器内所有Java方法参数是否有CurrentUser注解,若果有注解标识从NativeWebRequest中获取user信息进行参数绑定
shiro权限认证通过后进行页面跳转
1.判断是否认证通过 若未认证进入下一步 2.从ServletRequest获取回调url 3.获取默认回调url(客户端IP和端口) 4.将默认回调url保存到session中 5.将第2步中的回调url保存到ClientSavedRequest中(方便server回调时返回到当前请求url) 5.当前请求重定向到server端登录页面
ClientRealm继承自Shiro AuthorizingRealm 该类忽略doGetAuthenticationInfo方法实现,所有认证操作会转到Server端实现
实时更新、获取远程session
添加两个方法setFiltersStr、setFilterChainDefinitionsStr,方便在properties文件中配置拦截器和定义过滤链
通过Spring HttpInvokerServiceExporter工具将shiro-distributed-platform-api模块部分API暴露(remoteService、userService、resourceService),请参考spring-mvc-export-service.xml配置文件
配置ShiroFilterFactoryBean filterChainDefinitions属性将以上三个接口权限设置为游客、匿名(anon),请参考spring-config-shiro.xml配置文件
#各应用的appKey
client.app.key=1f38e90b-7c56-4c1d-b3a5-7b4b6ec94778
#远程服务URL地址
client.remote.service.url=http://127.0.0.1:8080 #根据实际应用地址配置
#登录地址
client.login.url=http://127.0.0.1:8080/login #根据实际应用地址配置
#登录成功后,默认重定向到的地址
client.success.url=/
#未授权重定向到的地址
client.unauthorized.url=http://127.0.0.1:8080/login #根据实际应用地址配置
#session id 域名
client.cookie.domain=
#session id 路径
client.cookie.path=/
#cookie中的session id名称
client.session.id=sid
#cookie中的remember me名称
client.rememberMe.id=rememberMe
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- Realm实现 -->
<bean id="remoteRealm" class="com.yz.shiro.client.ClientRealm">
<property name="cachingEnabled" value="false"/>
<property name="appKey" value="${client.app.key}"/>
<property name="remoteService" ref="remoteService"/>
</bean>
<!-- 会话ID生成器 -->
<bean id="clientSessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
<!-- 会话Cookie模板 -->
<bean id="clientSessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="${client.session.id}"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="-1"/>
<property name="domain" value="${client.cookie.domain}"/>
<property name="path" value="${client.cookie.path}"/>
</bean>
<bean id="clientRememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="${client.rememberMe.id}"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="2592000"/><!-- 30天 -->
<property name="domain" value="${client.cookie.domain}"/>
<property name="path" value="${client.cookie.path}"/>
</bean>
<!-- rememberMe管理器 -->
<bean id="ClientRememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<!-- rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)-->
<property name="cipherKey"
value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/>
<property name="cookie" ref="clientRememberMeCookie"/>
</bean>
<!-- 会话DAO -->
<bean id="clientSessionDao" class="com.yz.shiro.client.ClientSessionDAO">
<property name="sessionIdGenerator" ref="clientSessionIdGenerator"/>
<property name="appKey" value="${client.app.key}"/>
<property name="remoteService" ref="remoteService"/>
</bean>
<bean id="sessionFactory" class="com.yz.shiro.common.session.ShiroSessionFactory"/>
<!-- 会话管理器 -->
<bean id="clientSessionManager" class="com.yz.shiro.client.ClientWebSessionManager">
<property name="deleteInvalidSessions" value="false"/>
<property name="sessionValidationSchedulerEnabled" value="false"/>
<property name="sessionDAO" ref="clientSessionDao"/>
<property name="sessionIdCookieEnabled" value="true"/>
<property name="sessionIdCookie" ref="clientSessionIdCookie"/>
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 安全管理器 -->
<bean id="clientSecurityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="remoteRealm"/>
<property name="sessionManager" ref="clientSessionManager"/>
<property name="rememberMeManager" ref="ClientRememberMeManager"/>
</bean>
<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="clientSecurityManager"/>
</bean>
<bean id="clientAuthenticationFilter" class="com.yz.shiro.client.ClientAuthenticationFilter"/>
<bean id="sysUserFilter" class="com.yz.shiro.common.filter.SysUserFilter"/>
<!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="com.yz.shiro.client.ClientShiroFilterFactoryBean">
<property name="securityManager" ref="clientSecurityManager"/>
<property name="loginUrl" value="${client.login.url}"/>
<property name="successUrl" value="${client.success.url}"/>
<property name="unauthorizedUrl" value="${client.unauthorized.url}"/>
<property name="filters">
<util:map>
<entry key="authc" value-ref="clientAuthenticationFilter"/>
<entry key="sysUser" value-ref="sysUserFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/** = authc,sysUser
</value>
</property>
</bean>
<!-- Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<import resource="classpath:spring-client-remote-service.xml"/>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
metadata-complete="true" version="3.0">
<display-name>Archetype Created Web Application</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:ApplicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<!-- Shiro的filter必须放在其他filter之前 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<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>
<filter>
<filter-name>CharacterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
QQ交流群:776296081 Email:[email protected]