天天看點

Apache Shiro 整合Spring 進行權限驗證

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之前,否則不能成功使用) 

Apache Shiro 整合Spring 進行權限驗證

3.建立一個applicationContext-shiro.xml,對shiro進行配置,内容如下: 

Java代碼  

Apache Shiro 整合Spring 進行權限驗證
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.        xmlns:aop="http://www.springframework.org/schema/aop"  
  5.        xmlns:tx="http://www.springframework.org/schema/tx"  
  6.        xmlns:util="http://www.springframework.org/schema/util"  
  7.        xmlns:context="http://www.springframework.org/schema/context"  
  8.        xsi:schemaLocation="  
  9.        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
  10.        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd  
  11.        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd  
  12.        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd  
  13.        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">  
  14.     <!-- =========================================================  
  15.          Shiro Components  
  16.          ========================================================= -->  
  17.     <!-- Shiro's main business-tier object for web-enabled applications  
  18.          (use org.apache.shiro.web.mgt.DefaultWebSecurityManager instead when there is no web environment)-->  
  19.     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
  20.         <!-- Single realm app (realm configured next, below).  If you have multiple realms, use the 'realms'  
  21.       property instead. -->  
  22.     <!--這裡的sampleRealm需要我們自己實作,主要包括2個方法  
  23. 1.  使用者登入的驗證(授權)  
  24. 2.  使用者具有的角色和權限(認證)  
  25.  且看下面介紹-->  
  26.         <property name="realm" ref="sampleRealm"/>  
  27.         <!-- Uncomment this next property if you want heterogenous session access or clusterable/distributable  
  28.              sessions.  The default value is 'http' which uses the Servlet container's HttpSession as the underlying  
  29.              Session implementation.  
  30.         <property name="sessionMode" value="native"/> -->  
  31.     </bean>  
  32.     <!-- Post processor that automatically invokes init() and destroy() methods -->  
  33.     <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>  
  34.     <!-- 自定義角色過濾器 支援多個角色可以通路同一個資源 eg:/home.jsp = authc,roleOR[admin,user]  使用者有admin或者user角色 就可以通路-->  
  35.      <bean id="roleOR" class="com.yale.app.security.OneRoleAuthorizationFilter"/>  
  36.     <!-- Define the Shiro Filter here (as a FactoryBean) instead of directly in web.xml -  
  37.          web.xml uses the DelegatingFilterProxy to access this bean.  This allows us  
  38.          to wire things with more control as well utilize nice Spring things such as  
  39.          PropertiesPlaceholderConfigurer and abstract beans or anything else we might need: -->  
  40.     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
  41.         <property name="securityManager" ref="securityManager"/>  
  42.         <property name="loginUrl" value="/page/login.jsp"/>  
  43.         <property name="successUrl" value="/page/index.jsp"/>  
  44.         <property name="unauthorizedUrl" value="/register/unauthorized"/>  
  45.         <!-- The 'filters' property is usually not necessary unless performing an override, which we  
  46.              want to do here (make authc point to a PassthruAuthenticationFilter instead of the  
  47.              default FormAuthenticationFilter: -->  
  48.         <property name="filters">  
  49.             <util:map>  
  50.                 <entry key="authc">  
  51.                     <bean class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter"/>  
  52.                 </entry>  
  53.             </util:map>  
  54.         </property>  
  55.         <property name="filterChainDefinitions">  
  56.             <value>  
  57.                 /page/login.jsp = anon  
  58.                 /page/register  
  59. @Component  
  60. public class SampleRealm extends AuthorizingRealm {  
  61.      @Autowired  
  62.      private UserOperator userOperator;  
  63.     public SampleRealm() {  
  64.         setName("SampleRealm"); //This name must match the name in the User class's getPrincipals() method  
  65.       //  setCredentialsMatcher(new Sha256CredentialsMatcher());  
  66.         setCredentialsMatcher(new AllowAllCredentialsMatcher());  
  67.     }  
  68. //認證資訊,主要針對使用者登入,(下文講述在action或者controller登入過程代碼)  
  69.     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {  
  70.         UsernamePasswordToken token = (UsernamePasswordToken) authcToken;  
  71.         String  password = String.valueOf(token.getPassword());  
  72. //調用操作資料庫的方法查詢user資訊  
  73.         User user = userOperator.login( token.getUsername());  
  74.         if( user != null ) {  
  75.             if(password.equals(user.getPassword())){  
  76.                   Session session= SecurityUtils.getSubject().getSession();  
  77.                   session.setAttribute("username", user.getLoginName());  
  78.             return new SimpleAuthenticationInfo(user.getUserId(), user.getPassword(), getName());  
  79.             }else{  
  80.                 return null;  
  81.             }  
  82.         } else {  
  83.             return null;  
  84.         }  
  85.     }  
  86. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
  87.         String userId = (String) principals.fromRealm(getName()).iterator().next();  
  88.         User user = userOperator.getById(userId);  
  89.         if( user != null ) {  
  90.             SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();  
  91.            Role role = userOperator.getByRoleId(user.getRoleId());  
  92.                 info.addRole(role.getRoleName());  
  93.               //  info.addStringPermissions( role.getPermissions() );//如果你添加了對權限的表,打開此注釋,添加角色具有的權限  
  94.             return info;  
  95.         } else {  
  96.             return null;  
  97.         }  
  98.     }  
  99. }  

--注意shiro配置檔案中 

<bean id="roleOR" class="com.yale.app.security.OneRoleAuthorizationFilter"/> 

OneRoleAuthorizationFilter:為驗證多個角色可以通路同一個資源的定義: 

Java代碼  

Apache Shiro 整合Spring 進行權限驗證
  1. import java.io.IOException;  
  2. import java.util.Set;  
  3. import javax.servlet.ServletRequest;  
  4. import javax.servlet.ServletResponse;  
  5. import org.apache.shiro.subject.Subject;  
  6. import org.apache.shiro.util.CollectionUtils;  
  7. import org.apache.shiro.web.filter.authz.AuthorizationFilter;  
  8. public class OneRoleAuthorizationFilter extends AuthorizationFilter{  
  9.      @SuppressWarnings({"unchecked"})  
  10.         public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {  
  11.             Subject subject = getSubject(request, response);  
  12.             String[] rolesArray = (String[]) mappedValue;  
  13.             if (rolesArray == null || rolesArray.length == 0) {  
  14.                 //no roles specified, so nothing to check - allow access.  
  15.                 return true;  
  16.             }  
  17.             boolean flag = false;  
  18.             Set<String> roles = CollectionUtils.asSet(rolesArray);  
  19.             for (String string : roles) {  
  20.                 if(subject.hasRole(string)){  
  21.                     flag = true;  
  22.                 }  
  23.             }  
  24.             return flag;  
  25.         }  
  26. }  

4. 在工程WebRoot/page下,建立login.jsp,index.jsp,register.jsp; 

這裡主要說明index.jsp 

Shiro具有自己的JSP / GSP Tag Library,使用者做權限檢查判斷等等 

是以我們在index.jsp引入shiro 标簽庫 

Apache Shiro 整合Spring 進行權限驗證

裡面用很多的标簽,這裡我們主要說明: 

Apache Shiro 整合Spring 進行權限驗證

如果使用者具有administrator 角色我們就給他顯示這個連結 

Apache Shiro 整合Spring 進行權限驗證

如果使用者有user:create權限 我們顯示此連結 

根據我上文提到的user和role表中的資料,我們在index.jsp,做如下測試: 

<shiro:hasRole name="資料管理者”> 

您好管理者同志! 

    </shiro:hasRole> 

<shiro:hasRole name="普通使用者”> 

您好普通使用者! 

    </shiro:hasRole> 

5. jsp頁面寫完後,接下來我們看UserAction(使用者登入,退出等操作): 

Java代碼  

Apache Shiro 整合Spring 進行權限驗證
  1. //登入  
  2.         public String login(){  
  3. UsernamePasswordToken token = new UsernamePasswordToken(user.getName(), user.getPassword());  
  4.         try {  
  5.             SecurityUtils.getSubject().login(token);  
  6.         } catch (AuthenticationException e) {  
  7.             redirectPath="/page/login.jsp";  
  8.             return "redirect";  
  9.         }  
  10.         redirectPath="/page/index.jsp";  
  11.         return "redirect";  
  12.     }  
  13. //登出  
  14.     public String loginout(){  
  15.         SecurityUtils.getSubject().logout();  
  16.          redirectPath="/login.jsp";  
  17.             return "redirect";  
  18.     }  

至此基本結束,啟動項目,就可以體驗shiro的安全控制了 嘿嘿 

下面說freemarker中使用shiro标簽 

這個網上一搜尋就能找到答案,已經由James Gregory把代碼上傳到GitHub, 

位址:https://github.com/jagregory/shiro-freemarker-tags 

下載下傳該jar包 或者源代碼檔案複制到自己工程的lib下或者package中 

我是講檔案複制到自己的package中使用: 

Apache Shiro 整合Spring 進行權限驗證

如果你使用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代碼  

Apache Shiro 整合Spring 進行權限驗證
  1. import javax.servlet.ServletContext;     
  2. import org.apache.struts2.views.freemarker.FreemarkerManager;     
  3. import freemarker.template.Configuration;     
  4. import freemarker.template.TemplateException;     
  5. public class MyFreemarkerManager extends FreemarkerManager {     
  6.     @Override     
  7.     protected Configuration createConfiguration(ServletContext servletContext) throws TemplateException {     
  8.         Configuration cfg = super.createConfiguration(servletContext);     
  9.        cfg.setSharedVariable("shiro", new ShiroTags());  
  10.         return cfg;     
  11.     }     
  12. }  

然後在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内置過濾器研究-------------------------------------------

anon org.apache.shiro.web.filter.authc.AnonymousFilter
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port org.apache.shiro.web.filter.authz.PortFilter
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl org.apache.shiro.web.filter.authz.SslFilter
user org.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是第二組  

繼續閱讀