设为首页 收藏本站
查看: 699|回复: 0

[经验分享] Apache Shiro 整合Spring 进行权限验证

[复制链接]

尚未签到

发表于 2017-1-5 08:27:11 | 显示全部楼层 |阅读模式
  Apache Shiro是什么? 
Apache Shiro是一个功能强大且易于使用的Java安全框架,进行认证,授权,加密和会话管理。随着Shiro的易于理解的API,你可以快速,轻松地确保任何应用程序 - 移动应用从最小的到最大的Web和企业应用。 
如何使用Apache Shiro(这里指与Spring 集成)? 
1. 首先去官方网站下载相关jar包(这里使用1.2.2版本),其中包括: 
shiro-core-1.2.2.jar 
shiro-spring-1.2.2.jar 
shiro-web-1.2.2.jar 
(还需要slf4j相关jar支持) 
2.在web.xml中配置shiroFilter(注意这个filter一定要放在所有的filter之前,否则不能成功使用) 

DSC0000.jpg
3.创建一个applicationContext-shiro.xml,对shiro进行配置,内容如下: 
Java代码   DSC0001.png


  • <?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:aop="http://www.springframework.org/schema/aop"  
  •        xmlns:tx="http://www.springframework.org/schema/tx"  
  •        xmlns:util="http://www.springframework.org/schema/util"  
  •        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/tx http://www.springframework.org/schema/tx/spring-tx.xsd  
  •        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd  
  •        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd  
  •        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">  
  •     <!-- =========================================================  
  •          Shiro Components  
  •          ========================================================= -->  
  •   
  •     <!-- Shiro's main business-tier object for web-enabled applications  
  •          (use org.apache.shiro.web.mgt.DefaultWebSecurityManager instead when there is no web environment)-->  
  •     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
  •         <!-- Single realm app (realm configured next, below).  If you have multiple realms, use the 'realms'  
  •       property instead. -->  
  •     <!--这里的sampleRealm需要我们自己实现,主要包括2个方法  
  • 1.  用户登录的验证(授权)  
  • 2.  用户具有的角色和权限(认证)  
  •  且看下面介绍-->  
  •         <property name="realm" ref="sampleRealm"/>  
  •         <!-- Uncomment this next property if you want heterogenous session access or clusterable/distributable  
  •              sessions.  The default value is 'http' which uses the Servlet container's HttpSession as the underlying  
  •              Session implementation.  
  •         <property name="sessionMode" value="native"/> -->  
  •     </bean>  
  •   
  •     <!-- Post processor that automatically invokes init() and destroy() methods -->  
  •     <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>  
  •     <!-- 自定义角色过滤器 支持多个角色可以访问同一个资源 eg:/home.jsp = authc,roleOR[admin,user]  用户有admin或者user角色 就可以访问-->  
  •      <bean id="roleOR" class="com.yale.app.security.OneRoleAuthorizationFilter"/>  
  •     <!-- Define the Shiro Filter here (as a FactoryBean) instead of directly in web.xml -  
  •          web.xml uses the DelegatingFilterProxy to access this bean.  This allows us  
  •          to wire things with more control as well utilize nice Spring things such as  
  •          PropertiesPlaceholderConfigurer and abstract beans or anything else we might need: -->  
  •     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
  •         <property name="securityManager" ref="securityManager"/>  
  •         <property name="loginUrl" value="/page/login.jsp"/>  
  •         <property name="successUrl" value="/page/index.jsp"/>  
  •         <property name="unauthorizedUrl" value="/register/unauthorized"/>  
  •         <!-- The 'filters' property is usually not necessary unless performing an override, which we  
  •              want to do here (make authc point to a PassthruAuthenticationFilter instead of the  
  •              default FormAuthenticationFilter: -->  
  •         <property name="filters">  
  •             <util:map>  
  •                 <entry key="authc">  
  •                     <bean class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter"/>  
  •                 </entry>  
  •             </util:map>  
  •         </property>  
  •         <property name="filterChainDefinitions">  
  •             <value>  
  •                 /page/login.jsp = anon  
  •                 /page/register/* = anon  
  •                 /page/index.jsp = authc   
  •                 /page/addItem* = authc,roles[数据管理员]  
  •                 /page/file* = authc,roleOR[数据管理员,普通用户]  
  •                 /page/listItems* = authc,roleOR[数据管理员,普通用户]  
  •                 /page/showItem* = authc,roleOR[数据管理员,普通用户]  
  •                 /page/updateItem*=authc,roles[数据管理员]  
  •             </value>  
  •         </property>  
  •     </bean>  
  •   
  • </beans>  

  
其中的定义官网文档有详细的解释,这里不做描述! 
然后在applicationContext.xml中引入该文件! 

3. 实现sampleRealm,继承AuthorizingRealm,并重写认证授权方法 
1) 我们首先创建两张表User,Role(这里只做最简单的基于用户-角色的权限验证,如果需要细粒度的控制,自己在添加权限表Permissions然后进行关联操作) 
Java代码  


  • DROP TABLE IF EXISTS `users`;  
  • CREATE TABLE `users` (  
  •   `userid` int(10) NOT NULL AUTO_INCREMENT,  
  •   `loginName` varchar(16) DEFAULT NULL,  
  •   `password` varchar(16) DEFAULT NULL,  
  •   `mail` varchar(50) DEFAULT NULL,  
  •   `roleid` int(10) NOT NULL,  
  •   PRIMARY KEY (`userId`),  
  •   KEY `RoleId` (`roleid`),  
  •   CONSTRAINT `users_ibfk_1` FOREIGN KEY (`roleid`) REFERENCES `roles` (`RoleId`)  
  • ) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8;  
  • DROP TABLE IF EXISTS `roles`;  
  • CREATE TABLE `roles` (  
  •   `roleid` int(10) NOT NULL,  
  •   `roleName` varchar(50) DEFAULT NULL,  
  •   PRIMARY KEY (`RoleId`)  
  • ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  • --插入测试数据  
  • INSERT INTO `roles` VALUES ('1''普通用户');  
  • INSERT INTO `roles` VALUES ('2''数据管理员');  
  • INSERT INTO `users` VALUES ('1''yale''123456''yale@126.com''1');  
  • INSERT INTO `users` VALUES ('2''admin''admin''admin@126.com''2');  
  • 然后,创建对应的实体类,Dao操作,Service。这里不做细述  

  
接下来我们看sampleRealm具体内容: 
Java代码  


  • import org.apache.shiro.SecurityUtils;  
  • 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.UsernamePasswordToken;  
  • import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;  
  • import org.apache.shiro.authz.AuthorizationInfo;  
  • import org.apache.shiro.authz.SimpleAuthorizationInfo;  
  • import org.apache.shiro.realm.AuthorizingRealm;  
  • import org.apache.shiro.session.Session;  
  • import org.apache.shiro.subject.PrincipalCollection;  
  • import org.springframework.beans.factory.annotation.Autowired;  
  • import org.springframework.stereotype.Component;  
  •   
  • import com.yale.app.service.UserOperator;  
  • import com.yale.app.model.Role;  
  • import com.yale.app.model.User;  
  •   
  • /** 
  •  * The Spring/Hibernate sample application's one and only configured Apache Shiro Realm. 
  •  * 
  •  * <p>Because a Realm is really just a security-specific DAO, we could have just made Hibernate calls directly 
  •  * in the implementation and named it a 'HibernateRealm' or something similar.</p> 
  •  * 
  •  * <p>But we've decided to make the calls to the database using a UserDAO, since a DAO would be used in other areas 
  •  * of a 'real' application in addition to here. We felt it better to use that same DAO to show code re-use.</p> 
  •  */  
  • @Component  
  • public class SampleRealm extends AuthorizingRealm {  
  •       
  •      @Autowired  
  •      private UserOperator userOperator;  
  •   
  •     public SampleRealm() {  
  •         setName("SampleRealm"); //This name must match the name in the User class's getPrincipals() method  
  •       //  setCredentialsMatcher(new Sha256CredentialsMatcher());  
  •         setCredentialsMatcher(new AllowAllCredentialsMatcher());  
  •     }  
  •   
  •      
  • //认证信息,主要针对用户登录,(下文讲述在action或者controller登录过程代码)  
  •     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {  
  •         UsernamePasswordToken token = (UsernamePasswordToken) authcToken;  
  •         
  •         String  password = String.valueOf(token.getPassword());  
  • //调用操作数据库的方法查询user信息  
  •         User user = userOperator.login( token.getUsername());  
  •         if( user != null ) {  
  •             if(password.equals(user.getPassword())){  
  •                   Session session= SecurityUtils.getSubject().getSession();  
  •                   session.setAttribute("username", user.getLoginName());  
  •             return new SimpleAuthenticationInfo(user.getUserId(), user.getPassword(), getName());  
  •             }else{  
  •                 return null;  
  •             }  
  •         } else {  
  •             return null;  
  •         }  
  •     }  
  • protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
  •         String userId = (String) principals.fromRealm(getName()).iterator().next();  
  •         User user = userOperator.getById(userId);  
  •         if( user != null ) {  
  •             SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();  
  •            Role role = userOperator.getByRoleId(user.getRoleId());  
  •                 info.addRole(role.getRoleName());  
  •               //  info.addStringPermissions( role.getPermissions() );//如果你添加了对权限的表,打开此注释,添加角色具有的权限  
  •               
  •             return info;  
  •         } else {  
  •             return null;  
  •         }  
  •     }  
  •   
  • }  

  
--注意shiro配置文件中 
<bean id="roleOR" class="com.yale.app.security.OneRoleAuthorizationFilter"/> 
OneRoleAuthorizationFilter:为验证多个角色可以访问同一个资源的定义:
 
Java代码  


  • import java.io.IOException;  
  • import java.util.Set;  
  •   
  • import javax.servlet.ServletRequest;  
  • import javax.servlet.ServletResponse;  
  •   
  • import org.apache.shiro.subject.Subject;  
  • import org.apache.shiro.util.CollectionUtils;  
  • import org.apache.shiro.web.filter.authz.AuthorizationFilter;  
  •   
  • public class OneRoleAuthorizationFilter extends AuthorizationFilter{  
  •   
  •      @SuppressWarnings({"unchecked"})  
  •         public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {  
  •   
  •             Subject subject = getSubject(request, response);  
  •             String[] rolesArray = (String[]) mappedValue;  
  •   
  •             if (rolesArray == null || rolesArray.length == 0) {  
  •                 //no roles specified, so nothing to check - allow access.  
  •                 return true;  
  •             }  
  •             boolean flag = false;  
  •             Set<String> roles = CollectionUtils.asSet(rolesArray);  
  •             for (String string : roles) {  
  •                 if(subject.hasRole(string)){  
  •                     flag = true;  
  •                 }  
  •             }  
  •             return flag;  
  •         }  
  •   
  • }  

  
4. 在工程WebRoot/page下,创建login.jsp,index.jsp,register.jsp; 
这里主要说明index.jsp 
Shiro具有自己的JSP / GSP Tag Library,用户做权限检查判断等等 
所以我们在index.jsp引入shiro 标签库 

DSC0002.jpg  
里面用很多的标签,这里我们主要说明: 

DSC0003.jpg  
如果用户具有administrator 角色我们就给他显示这个链接 

DSC0004.jpg  
如果用户有user:create权限 我们显示此链接 
根据我上文提到的user和role表中的数据,我们在index.jsp,做如下测试: 
<shiro:hasRole name="数据管理员”> 
您好管理员同志! 
    </shiro:hasRole> 
<shiro:hasRole name="普通用户”> 
您好普通用户! 
    </shiro:hasRole> 
5. jsp页面写完后,接下来我们看UserAction(用户登录,退出等操作): 
Java代码  


  • //登录  
  •         public String login(){  
  • UsernamePasswordToken token = new UsernamePasswordToken(user.getName(), user.getPassword());  
  •         try {  
  •             SecurityUtils.getSubject().login(token);  
  •                   
  •         } catch (AuthenticationException e) {  
  •             redirectPath="/page/login.jsp";  
  •             return "redirect";  
  •         }  
  •         redirectPath="/page/index.jsp";  
  •         return "redirect";  
  •     }  
  • //注销  
  •     public String loginout(){  
  •         SecurityUtils.getSubject().logout();  
  •          redirectPath="/login.jsp";  
  •             return "redirect";  
  •     }  

  
至此基本结束,启动项目,就可以体验shiro的安全控制了 嘿嘿 

下面说freemarker中使用shiro标签 
这个网上一搜索就能找到答案,已经由James Gregory把代码上传到GitHub, 
地址:https://github.com/jagregory/shiro-freemarker-tags 
下载该jar包 或者源代码文件复制到自己工程的lib下或者package中 
我是讲文件复制到自己的package中使用: 


DSC0005.png  

如果你使用spring MVC 
请看http://www.woxplife.com/articles/473.html 
如果你单独使用Freemarker 比如使用模板生成静态页 
在相关的类中加入如下代码: 
Configuration cfg = new Configuration(); 
cfg.setDefaultEncoding(“UTF-8”); 
cfg.setSharedVariable("shiro", new ShiroTags()); 
然后在ftl页面中使用tag: 
<@shiro.hasRole name=”admin”>Hello admin!</@shiro.hasRole> 
如果是使用struts2集成的freemarker作为页面渲染 
可以写一个类extend    Struts2的FreemarkerManager: 
Java代码  


  • import javax.servlet.ServletContext;     
  •      
  • import org.apache.struts2.views.freemarker.FreemarkerManager;     
  •      
  • import freemarker.template.Configuration;     
  • import freemarker.template.TemplateException;     
  •      
  • public class MyFreemarkerManager extends FreemarkerManager {     
  •      
  •     @Override     
  •     protected Configuration createConfiguration(ServletContext servletContext) throws TemplateException {     
  •         Configuration cfg = super.createConfiguration(servletContext);     
  •        cfg.setSharedVariable("shiro"new ShiroTags());  
  •         return cfg;     
  •     }     
  • }  

  
然后在struts.xml中指定struts使用我们自己扩展的 FreemarkerManager 
<constant name="struts.freemarker.manager.classname"   
    value="com.xxx.xxx.MyFreemarkerManager" />  
然后在页面中 
然后在ftl页面中使用tag: 
<@shiro.hasRole name=”admin”>Hello admin!</@shiro.hasRole> 


  ----------------------------shiro内置过滤器研究-------------------------------------------
anonorg.apache.shiro.web.filter.authc.AnonymousFilter
authcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
portorg.apache.shiro.web.filter.authz.PortFilter
restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter
sslorg.apache.shiro.web.filter.authz.SslFilter
userorg.apache.shiro.web.filter.authc.UserFilter
 
rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString
是你访问的url里的?后面的参数。
perms:例子/admins/user/**=perms[user:add:*],perms参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于
isPermitedAll()方法。
roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如/admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
anon:例子/admins/**=anon 没有参数,表示可以匿名使用。
authc:例如/admins/user/**=authc表示需要认证才能使用,没有参数
authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证
ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
 
这些过滤器分为两组,一组是认证过滤器,一组是授权过滤器。其中anon,authcBasic,auchc,user是第一组,
perms,roles,ssl,rest,port是第二组
 

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-323993-1-1.html 上篇帖子: Apache下的虚拟主机设置 下篇帖子: Apache和tomcat多项目整合发布
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表