最近公司需要增加角色权限管理模块,由我负责开发,经过慎重考虑,我舍弃了自己写一个权限管理框架的傻叉想法(还当上学呢),而是用了开源的shiro去实现。由于经验不足,搞了几天才弄懂,终于也算是搭建上了,实现了角色权限管理,大白话就是能校验登录信息是否正确,根据数据库给用户赋予角色,登录后根据角色展示不同的模块信息。但是不足的是,由于需求没要求,也是自己没搞定,就是我现在无法给角色分配权限,权限可以分配到用户上,所以权限和角色就成了一个平行的关系(但事实上应该不是这样),还有待研究,有经验的大牛可以给小弟指点指点,不胜感激。
言归正传,搭建框架第一步要有相应的资源,感谢资源提供者【尚硅谷教育】。链接: http://pan.baidu.com/s/1sjvJtJR 密码: 47c3,百度云盘,上面有详细的视频和例子,jar包。我基本就是看着这个视频搭建的(也借助了网上很多文章和demo项目)。但是,要只看视频就ok的话,这篇博客就到此为止了,但下面才是我在搭建时遇见的各种问题,以及我解决问题的思路和方法,甚至有些问题依然没有解决,我只是绕过了他实现了需求。follow my thought。
先把基础的搭建说下吧(用spring管理shiro配置方案):
首先在web.xml里面完成spring基础配置,然后配置Shiro的FilFilter
<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>
然后在classpath下创建shiro自己的配置文件
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.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.atguigu.shiro.realm.MyRealm"></bean>
<!--配置shiro的securityManager Bean -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<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文件中filter的name一致 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 装配 securityManager-->
<property name="securityManager" ref="securityManager"/>
<!-- 配置登录页面 -->
<property name="loginUrl" value="/shiro-login.jsp"/>
<!-- 登录成功之后的页面 -->
<property name="successUrl" value="/shiro-success.jsp"/>
<!-- 授权失败页面 -->
<property name="unauthorizedUrl" value="/unauthorized.jsp"/>
<!-- 具体配置需要拦截哪些URL,以及访问对应的URL时 -->
<property name="filterChainDefinitions">
<!-- anon表示 可以 匿名登录 -->
<!-- authc表示 需要认证(登录)才能使用 -->
<!-- roles 角色过滤器,判断当前用户是否指定角色 -->
<value>
/shiro-logout = logout
/shiro-* = anon
<!-- /user.jsp = roles[user] -->
/user.jsp = perms[userCm]
<!-- /admin.jsp = roles[admin] -->
/admin.jsp = perms[adminCm]
/** = authc
</value>
</property>
</bean>
</beans>
还有spring、springmvc和ehcache的配置文件就不在这里列出了,稍后我会给出github源码链接,我自己完善好的代码。
然后我们需要创建一个类去继承AuthorizingRealm,实现doGetAuthorizationInfo(授权)和doGetAuthenticationInfo(认证)方法。
在controller中这样写,执行login时就自动调用认证和授权方法了,认证即比对输入的账号密码与数据库的是否一致,授权就是根据数据库分配给用户相应的角色权限
<span style="white-space:pre"> </span>Subject subject = SecurityUtils.getSubject();
<span style="white-space:pre"> </span>UsernamePasswordToken token = new UsernamePasswordToken(username,password);
<span style="white-space:pre"> </span>subject.login(token);
下面讲下授权与认证方法怎么写
<span style="white-space:pre"> </span>/**
* 认证方法
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
System.out.println("-----进入认证方法----by:cuimiao demo");
//登录的主信息:从数据库中查询的结果,该结果和token中携带的必须一致
String principal = (String) token.getPrincipal();
//认证信息:从数据库中查询出来的信息。密码的比对交给shiro去进行比较
String credentials = "123456";
//当前realm的name
String realmName = getName();
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);
System.out.println("-----认证方法结束----by:cuimiao demo");
return info;
}
很神奇的是执行subject.login(token);的时候就调用了认证方法,认证通过后就带着info调用授权方法
<span style="white-space:pre"> </span>/**
* 授权方法
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("-----进入授权方法----by:cuimiao demo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Object principal = principalCollection.getPrimaryPrincipal();
if("admin".equals(principal)){
// info.addRole("admin");
info.addStringPermission("adminCm");
}
if("user".equals(principal)){
// info.addRole("test");
info.addStringPermission("testCm");
}
// info.addRole("user");
info.addStringPermission("userCm");
System.out.println("-----授权方法结束----by:cuimiao demo");
return info;
}
我们可以把在数据库的相应的权限放到info中,然后info会带着这权限,每次进入页面时检测shiro配置文件,判断该权限是否可以登录该页面。
然后前端jsp上引用标签<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>,然后
<span style="white-space:pre"> </span><shiro:hasAnyRoles name="user">
<a href="user.jsp">User Page</a>
</shiro:hasAnyRoles>
就可以判断 只有user角色可以访问<a href="user.jsp">User Page</a>这个链接了。
大体流程就是这样了。
首先第一个坑是springMVC配置文件编译不通过,查了多方资料后发现是jar包有问题,我把spring的jar包重新下了一份,导入后解决问题。
第二个坑是demo中执行subject.login(token)时都自动调用授权方法,但在正式项目中,死活都不走授权,只走认证,多方查找资料,有的说是缓存原因,有的说是配置原因,具体什么原因我没查出来,诸位网友可以踩在我的肩膀上帮我看看是因为什么(稍后我会提供两个demo,一个是自动走授权的,一个是不自动走的)。如果解决了,告诉老兄,不胜感激!(联系方式-微信:cuimiao147)
那解决不了不能不授权啊,我就选择了一个最笨的法,虽然也解决了问题,但不知道是否后期有隐患。
就是手动显式的调用授权方法,但由于授权方法是protected的,所以我需要对它稍微加工下
<span style="white-space:pre"> </span>public void doGetAuthorizationInfoByAdmin(PrincipalCollection principalCollection) {
this.doGetAuthorizationInfo(principalCollection);
}
这样我们就可以在subject.login(token);执行结束后手动调用授权方法了,调用的代码是
testRealm.doGetAuthorizationInfoByAdmin(subject.getPrincipals());
把他们放在一个try块中,认证不通过时就抛异常不执行授权方法了,通过时则会给用户授权。
github链接:
可用demo,会自动调用授权方法,不带数据库
网上demo,不自动调用授权方法,带数据库脚本(网友们寻找原因哈)