项目简介
背景 及 需求
-
背景
- 平台融合了多种用户(平台客户、后台运营者)
- 多种登录逻辑(面向客户的、面向运营人员的)混杂在一个项目中 需求
- 为后台运营系统增加权限
- 页面元素按照用户角色决定是否展示 难点
- shiro难以对多个登陆逻辑、多个系统做出统一的处理,很难拆分出 不需要shiro 管理的面向客户的系统,也就是说需要拦截的东西是不确定的,不需要拦截的东西是确定的。
解决思路
-
想法一
- 刚开始想法错误,考虑到以后需求可能会将客户平台也纳入到shiro中,所以总想着将所有东西都划归到shiro下管理,但是当前系统需要拦截的东西是不确定的,反而不需要拦截的东西是确定的。走了很多弯路。
- 其中 loginUrl 即未授权重定向的页面是对应多个的,所以只能通过一个Action或Filter来做复杂判断和跳转。
-
shiro 核心过滤器如下
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/loginDispatcher.do"/>
<property name="unauthorizedUrl" value="/page/401"/>
<property name="filterChainDefinitions">
<value>
<!-- 静态资源允许访问 -->
/resource/** = anon
<!--用户平台允许访问-->
/bank/** = anon
<!--运营平台需要登录认证-->
/platform/** = authc
<!-- 其他资源需要认证 -->
/** = authc
</value>
</property>
</bean>
想法二
-
由于前台mvc层使用的是Struts2(各种漏洞、吐槽)、而且考虑到当前struts2的命名空间划分还是比较合理的,运营平台统一划分在
/platform
下,转变思路,不去考虑使用shiro管理所有东西,把所有权限都放行,只拦截/platform
下的。 - 客户平台还是由原来的Filter管理
-
struts.xml
<!-- 运营平台 --> <package name="platform" namespace="/platform" extends="struts-default"> <action> ... </package >
-
shrio核心过滤器
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login.jsp"/> <property name="unauthorizedUrl" value="/page/401"/> <property name="filterChainDefinitions"> <value> <!--后台运营使用shrio管理--> /platform/** = authc <!-- 其他资源放开 --> /** = anon </value> </property> </bean>
环境
- JDK1.7
- Tomcat1.8
- Struts2 2.32
- Spring 4.0.6
- Hibernate 4.0.4
- shiro 1.2.3
配置
-
web.xml
-
shiro的过滤器应该位于MVC(这里是struts2)层框架过滤器的上方
<!-- shiro的过滤器 --> <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> <!-- struts2的过滤器 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <!-- shiro的过滤器 --> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- struts2的过滤器 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter>
自定义登录逻辑 Realm
-
继承AuthorizingRealm
public class PlatformRealm extends AuthorizingRealm { ... }
-
重写登录教研
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { ... }
-
重写授权校验
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { ... }
注册Realm给安全管理器
-
DefaultWebSecurityManager
安全管理器<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realms"> <list> <ref bean="platformRealm"/> </list> </property> </bean>
~使用Shiro~
-
-
登录逻辑重构
UsernamePasswordToken
- 用户令牌
UsernamePasswordToken token = new UsernamePasswordToken(); token.setUsername("用户输入的用户名"); token.setPassword("用户输入的密码");
-
Subject
- 可以理解为Shiro所管理的用户对象所
Subject subject = SecurityUtils.getSubject(); //登录 subject.login(token);
-
SimpleAuthenticationInfo
-
认证及认证结果信息,其中user为登陆成功后 返还给客户端的用户对象。可以通过
User user = (User) subject.getPrincipal();
获取。
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, "数据库中的密码", getName());
权限校验
注解方式
: @RequiresPermissions(“你定义的权限名称”)应用在你的方法上。页面标签
:<shiro:hasPermission name="user:create">
等
自定义url过滤器
- shiro内置的 角色url过滤器校验的逻辑为且的关系,即满足多种角色才可以,一个用户即是platform.admin角色而且也是platform.operator角色才可以访问,代码如下
先在的需求:需要两者满足其一即可,即或的关系。
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="filterChainDefinitions"> <value> /platform/home.do* = roles[platform.admin,platform.operator]
自定义过滤器
参考自ikaraide的博客public class CustomRolesAuthorizationFilter extends AuthorizationFilter { @Override protected boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object mappedValue) throws Exception { Subject subject = getSubject(req, resp); String[] rolesArray = (String[]) mappedValue; if (rolesArray == null || rolesArray.length == 0) { //没有角色限制,有权限访问 return true; } for (String aRolesArray : rolesArray) { if (subject.hasRole(aRolesArray)) { //若当前用户是rolesArray中的任何一个,则有权限访问 return true; } } return false; } }
配置
<!-- 自定义的过滤器,用来判断当前用户是否是roleOrFilter["comm,test"]中的某个角色 --> <bean id="roleOrFilter" class="cn.ecotrans.web.shior.CustomRolesAuthorizationFilter" />
注册该过滤器到shiro的过滤器链
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 添加各种验证过滤器 --> <property name="filters"> <map> <entry key="roleOrFilter" value-ref="roleOrFilter"/> </map> </property> ··· ··· ···
使用
/platform/** = roleOrFilter[platform.admin,platform.operator]