在項目中集成shiro權限框架

在項目中集成shiro權限框架(1)

 

Shiro是一個功能強大的輕量級權限框架,相對其它權限框架(比如spring security)來說,要易用得很,下面,我給大家講講如何在一個項目中簡單整合shiro。

我們通常所說的權限,就是要判斷某個操作者是否有操作某個資源的權限,而資源,可以是菜單、鏈接、功能按鈕、業務方法、某類型的數據等等,根據需求,每個項目的權限可能都有所不同,通用萬能權限系統是不存在的。

 

Shrio的權限包括認證、授權、密碼管理、會話管理等四個部分,每一個部分,shrio都進行了抽象,以做到和具體應用平臺、環境的無關。

權限系統的設計,根據不同的權限粒度,會用所不同,比如:有的系統到菜單級,而有的到每一個功能按鈕級,有的到方法級,有的到業務數據級,我在這僅僅討論前兩類。

 

 

上面是一個對象關係圖,模塊在此處僅僅爲菜單進行邏輯歸類,當然,也可以建立菜單和功能的一對多關係,爲功能進行邏輯歸類。當然,菜單也可以建成關聯的樹狀結構,以更好擴展

 

 

根據對象關係,可以設計出表結構,我直接貼出數據截圖:

模塊表:

菜單表:

 

 

 操作表,一般對應頁面中的按鈕,其中f_action_flag對應於restful中的增、刪、查、改:

 

 

角色表:

 

 

角色-菜單關係表:

 

 

角色-功能表:

 

 

用戶表:

 

 

用戶-角色關係表:

 

 

上面截圖中,真正有效的表7張,權限控制到頁面及功能上。

下篇,貼出上面的類代碼及class映射文件。

在項目中集成shiro權限框架(2)

基礎代碼貼上:

對象如下:

/**

 * 用戶對象

 * 

 * @authorLjh

 * 

 */

public classUser implementsSerializable {

 

    // 學生編碼(由系統自動生成)

    privateLong id;

 

    // 姓名

    @NotEmpty(message = "姓名不可以爲空!")

    @Length(min = 2, max = 5)

    privateString name;

 

    // 出生日期

    @NotNull(message = "生日不能爲空")

    @Past(message = "生日輸入不正確,請覈對!")

    privateDate birthday;

 

    // 登錄密碼

    privateString password"111";

 

    // 用戶角色集合字符

    privateSet<Role> rolesnewHashSet<Role>();

    

    /**

     * 得到該用戶可訪問的模塊,模塊中的菜單將實例化

     * @return

     */

    publicList<Model> getModels() {

       List<Model> models = newArrayList<Model>();

       for(Iterator<Menu> its = this.getMenus().iterator(); its.hasNext();){

           Menu m = its.next(); 

           if(!models.contains(m.getModel())) {

              Model model = newModel();

              model.setId(m.getModel().getId());

              model.setDisplayOrder(m.getDisplayOrder());

              model.setModelName(m.getModel().getModelName());

              model.getMenus().add(m);

              models.add(model);

           } else{

              for(Model model : models) {

                  if(model.getId() == m.getModel().getId()) {

                     model.getMenus().add(m);

                     break;

                  }

              }

           }

       }

       Collections.sort(models);

       returnmodels;

    }

    

    /**

     * 得到用戶可訪問的菜單資源

     * @return

     */

    publicSet<Menu> getMenus() {

       Set<Menu> menus = newHashSet<Menu>();

       for(Iterator<Role> role = this.getRoles().iterator(); role.hasNext();) {

           menus.addAll(role.next().getMenus());

       }

       returnmenus;

       

    }

    

    // 用戶菜操作功能權限集合字符串描述

    publicSet<String> getOperationPermissionsAsString() {

       Set<String> pomissions = newHashSet<String>();

       Iterator<Role> roles = this.getRoles().iterator();

       

       Operation op;

       

       Map<String, HashSet<String>> p_map = newHashMap<String,HashSet<String>>();

       

       while(roles.hasNext()) {

           Iterator<Operation> operations =roles.next().getOperations().iterator();

           while(operations.hasNext()) {

              op = operations.next();

              String key = op.getUrl();

              if(!key.startsWith("/")) {

                  key = "/"+ key;

              }

              if(p_map.get(key) == null) {

                  p_map.put(key, newHashSet<String>());

              }

              p_map.get(key).add(op.getActionFlag());

           }

       }

       //構建形如:[doc:read, moveuser:modify, users:read,user:modify,read,create]的權限字串

       for(Entry<String, HashSet<String> > entry :p_map.entrySet()) {

           pomissions.add(entry.getKey() + ":"+ entry.getValue().toString().replace("[""").replace("]""").replace(" """));

       }

       

       returnpomissions;

    }

 

    // 用戶菜單權限集合字符串描述

//  public Set<String> getMenuPermissionsAsString() {

//     Set<String> pomissions= newHashSet<String>();

//     Iterator<Role> roles = this.getRoles().iterator();

//     while (roles.hasNext()) {

//         Iterator<Menu> menus =roles.next().getMenus().iterator();

//         while (menus.hasNext()) {

//            pomissions.add(menus.next().getCode());

//         }

//     }

//     return pomissions;

//  }

    /**

     * 得到我的全部權限

     * @return

     */

    publicSet<String> getPermissionsAsString() {

       Set<String> permissions = newHashSet<String>();

       //permissions.addAll(getMenuPermissionsAsString());

       permissions.addAll(getOperationPermissionsAsString());

       return  permissions;

    }

 

    // 得到用戶角色字符串描述

    publicSet<String> getRolesAsString() {

       Set<String> str_roles = newHashSet<String>();

       Iterator<Role> roles = this.getRoles().iterator();

       while(roles.hasNext()) {

           str_roles.add(roles.next().getRoleCode());

       }

       returnstr_roles;

    }

 

 

角色對象:

 

public classRole implementsSerializable {

    //OID

    private int id;

    //顯示名稱

    privateString displayName;

    //角色編碼,用於生成權限框架的惟一標識

    privateString roleCode;

    //角色可以操作的菜單

    privateSet<Menu> menusnewHashSet<Menu>();

    //角色可操作的操作功能【對應於增、刪、查、改等功能】

    privateSet<Operation> operationsnewHashSet<Operation>();

    

 

菜單對象:

 

public classMenu implementsResource, Comparable, Serializable  {

    //OID

    private int id;

    privateString code;

    privateString name;

    //顯示順序

    private int displayOrder;

    //url地址

    privateString url;

    //所屬模塊

    privateModel model;

 

 

@Override

    public intcompareTo(Object menu) {

       Menu m = (Menu)menu;

       returnm.getDisplayOrder() - this.getDisplayOrder();

    }

 

操作功能對象:

public classOperation implementsResource, Serializable{

    

    //OID

    private int id;

    //操作碼(保留待用)

    privateString code;

    //名稱

    privateString name;

    

    //操作標誌read:讀取,create:新增,modify:修改,delete:刪除

    //rest風格中,將請求方式映射爲上述四種操作,設計爲字符串,以方便今後擴展

    privateString actionFlag;

    

    //操作url地址,比如:/user/*

    privateString url;

    

    //所屬菜單

    privateMenu menu;

    //顯示順序,保留待用

    private int displayOrder;

 

 

功能模塊對象:

/**

 * 功能模塊

 * @authorAdministrator

 *

 */

public classModel implementsComparable<Model>,Serializable {

//public class Model  {

    private int id;

    privateString modelName;

    

    //顯示順序

    private int displayOrder;

    

    privateSet<Menu> menusnewHashSet<Menu>();

 

資源抽象對象,目前暫未使用:

/**

 * 資源抽象類

 * @authorAdministrator

 *

 */

public interfaceResource {

    //得到資源編碼

    publicString getResCode();

 

}

 

 

最後,貼上hbm文件。

<classname="com.my.model.User"table="t_student">

        <idname="id">

            <columnname="pk_id"/>

            <generatorclass="identity"></generator>

        </id>

        

        <propertyname="name"type="java.lang.String">

            <columnname="f_name"/>

        </property>

 

        <propertyname="password"type="java.lang.String">

            <columnname="f_password"/>

        </property>

        

        <propertyname="birthday">

            <columnname="f_birthday"/>

        </property>

        

        <setname="roles"table="t_student_role">

         <keycolumn="fk_student_id"></key>

         <many-to-manyclass="com.my.model.Role"column="fk_role_id">

         </many-to-many>

        </set>

</class>

 

<classname="com.my.model.Model"table="t_model">

        <idname="id">

            <columnname="pk_id"/>

            <generatorclass="identity"></generator>

        </id>

        <propertyname="modelName"type="java.lang.String">

            <columnname="f_model_name"/>

        </property>

        

        <propertyname="displayOrder"column="f_display_order">

        </property>

        

        <setname="menus"table="t_menu"order-by="f_display_order">

         <keycolumn="fk_model_id"></key>

         <one-to-manyclass="com.my.model.Menu"/>

        </set>

    </class>

    

    <classname="com.my.model.Menu"table="t_menu">

        <idname="id">

            <columnname="pk_id"/>

            <generatorclass="identity"></generator>

        </id>

        

        <propertyname="code"type="java.lang.String">

            <columnname="f_code"/>

        </property>

 

        <propertyname="name"type="java.lang.String">

            <columnname="f_name"/>

        </property>

        

        <propertyname="displayOrder"column="f_display_order">

        </property>

        

         <propertyname="url"type="java.lang.String">

            <columnname="f_url"/>

        </property>

        <many-to-onename="model"class="com.my.model.Model"column="fk_model_id">

        </many-to-one>

    </class>

    

    <classname="com.my.model.Operation"table="t_operation">

        <idname="id">

            <columnname="pk_id"/>

            <generatorclass="identity"></generator>

        </id>

        

        <propertyname="code"type="java.lang.String">

            <columnname="f_code"/>

        </property>

 

        <propertyname="name"type="java.lang.String">

            <columnname="f_name"/>

        </property>

        

        <propertyname="displayOrder"column="f_display_order">

        </property>

        

        <propertyname="actionFlag"column="f_action_flag">

        </property>

        

         <propertyname="url"type="java.lang.String">

            <columnname="f_url"/>

        </property>

        

        <many-to-onename="menu"class="com.my.model.Menu"column="fk_menu_id">

        </many-to-one>

    </class>

 

    <classname="com.my.model.Role"table="t_role">

        <idname="id">

            <columnname="pk_id"/>

            <generatorclass="identity"></generator>

        </id>

        

        <propertyname="displayName"type="java.lang.String">

            <columnname="f_displayName"/>

        </property>

 

        <propertyname="roleCode"type="java.lang.String">

            <columnname="f_roleCode"/>

        </property>

 

        <setname="menus"table="t_role_menu">

         <keycolumn="fk_role_id"></key>

         <many-to-manyclass="com.my.model.Menu"column="fk_menu_id"order-by="f_display_order">

         </many-to-many>

        </set>

        

         <setname="operations"table="t_role_operation">

         <keycolumn="fk_role_id"></key>

         <many-to-manyclass="com.my.model.Operation"column="fk_operation_id"order-by="f_display_order">

         </many-to-many>

        </set>

    </class>

 

基礎的代碼完成了,其實,user中已經有部分和權限驗證相關的代碼了,後面我詳細講解。

在項目中集成shiro權限框架(3)

Shiro與spring已經有完整的整合方法,所以,我們先在web.xml中添對過濾器,將需要驗證的請求,攔截到shiro中。

<!—shiro過濾器-->

    <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>

 

在spring配置中添加下面的支持bean.

<!-- 權限or判定器-->

    <beanid="roleOrFilter"class="com.my.commons.RolesOrFilter">

    </bean>

    

    <!-- 認證數據庫存儲-->

    <beanid="myRealm"class="com.my.service.impl.DbAuthRealm">

    </bean>

 

    <!-- 權限管理器-->

    <beanid="securityManager"class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

       <propertyname="realms">

           <list>

              <refbean="myRealm"/>

           </list>

       </property>

    </bean>

 

    <beanid="shiroFilter"class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"depends-on="roleOrFilter">

    

       <propertyname="securityManager"ref="securityManager"/>

 

       <propertyname="loginUrl"value="/login.jsp"/>

       <propertyname="successUrl"value="/main"/>

       <propertyname="unauthorizedUrl"value="/commons/unauth.jsp"/>

       

       <!-- 讀取自定義權限內容-->

       <propertyname="filterChainDefinitions"value="#{authService.loadFilterChainDefinitions()}"/>   

       <propertyname="filters">

           <map>

              <entrykey="roleOrFilter"value-ref="roleOrFilter">

              </entry>

           </map>

       </property>

    </bean>

 

    <beanid="lifecycleBeanPostProcessor"class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

 

 

 

其中filterChainDefinitions部分,簡單的項目可以直接寫死,我們這裏採用的是啓動時讀取生成,如果寫死,將會出現類似下面的代碼:

<property name="filterChainDefinitions">

           <value>

              /js/** = anon

              /css/** = anon

              /login.jsp = anon

              /login = anon

              /docs/doc1.jspauthc,roleOrFilter[admin,dev]

              /admin/manager.jsp = authc, roleOrFilter[admin]

              /admin/user/* = authc, rest[/admin/user/*]

              /userinfo/myinfo.jsp = authc,roleOrFilter[test,dev,admin]

              /** = authc

           </value>

       </property>

 

這段靜態權限代碼中用到了過濾器,我得給大家說一下:

anon部分是不需要進行驗證的,即這部分是要放行的(注意**與*是有區別的,**表示該目錄下的所有,當然也包括子目錄下的東西,而*僅指當前目錄下的東西)。

Authc,這個是需要登錄的人纔可以訪問的。

roleOrFile,這個是我定義的,只要這個過濾器中的一個角色滿足,即可訪問,而shiro提供的role過濾器,是當所有角色都滿足時纔可訪問。

Rest是restful過濾器,會將get,post,put,delete轉換爲對資源的read.create,modify,delete操作。

當然,shiro還提供了其它的過濾器,大家可以自己去看看,比如:permission過濾器,shiro對權限的描述採用wildcard字串,功能強大且可讀性強。

另外一點要接出,shiro在裏使用最先匹配規則,一旦匹配成功,將不再進行後續的過濾規則檢查,因此,在書寫時一定要注意順序,比如,你把/** = anon寫到第一行,那麼後面的一切都將不會再檢測。

好了,下面來講如何動態生成這個規則,大家看我的配置中有value="#{authService.loadFilterChainDefinitions()}",這個是spring el表達式語言,表示調用容器中的另一個bean的方法,把這個方法的返回結果,賦值給filterChainDefinitions屬性。

在些再補充一些權限字串的知識:

shiro所支持的廣義權限字串表達式,共有三種:

1、簡單方式

比如:subject.isPermitted("editNews"),表示判斷某操作者是否有【編輯新聞】的權限。

2、細粒度方式

比如:subject.isPermitted("News:create"),表示判斷某操作者是否有【新建新聞】的權限。

3、實例級訪問方式

比如:subject.isPermitted("News:edit:10"),表示判斷某操作者是否有【編輯id號是10新聞】的權限。

上面3種方式中,可以用*表示所有,例如:"News:*"爲對所有新聞的操作,"*:create"對所有事務都可以新增。還可以用 逗號 表示或都,"News:edit:10,11"表示可對10,11號新聞進行編輯。

如果要寫頁面權限,可參照如下配置:

/index.jsp = anon
/admin/** = authc, roles[admin]
 /docs/** = authc, perms[document:read]
 /** = authc

 

我定義了一個權限相關的接口,如下:

/**

 * 權限管理相關方法

 * @authorljh

 *

 */

public interfaceIAuthService{

    /**

     * 加載過濾配置信息

     * @return

     */

    publicString loadFilterChainDefinitions();

    

    /**

     * 重新構建權限過濾器

     * 一般在修改了用戶角色、用戶等信息時,需要再次調用該方法

     */

    public voidreCreateFilterChains();

 

}

 

 

其中一個方法用於加載生成權限規則字串,另一個,用於用戶在系統中更改了角色-菜單,角色-功能關係時,動態重新生效的方法,實現類如下:

@Service(value="authService")

public classAuthServiceImplimplementsIAuthService {

    

    private static finalLogger log= Logger.getLogger(AuthServiceImpl.class);

    

    //注意/r/n前不能有空格

    private static finalString CRLF"\r\n";

    private static finalString LAST_AUTH_STR"/** =authc\r\n";

    

    @Resource

    privateShiroFilterFactoryBean shiroFilterFactoryBean;

    

    @Resource

    privateIBaseDao dao;

 

    @Override

    publicString loadFilterChainDefinitions() {

 

       

       StringBuffer sb = newStringBuffer("");

       sb.append(getFixedAuthRule())

       .append(getDynaAuthRule())

       .append(getRestfulOperationAuthRule())

       .append(LAST_AUTH_STR);

 

       

       returnsb.toString();

    }

    

    //生成restful風格功能權限規則

    privateString getRestfulOperationAuthRule() {

       

       List<Operation> operations = dao.queryEntitys("from Operation o"newObject[]{});

       

       Set<String> restfulUrls = newHashSet<String>();

       for(Operation op : operations) {

           restfulUrls.add(op.getUrl());

       }

       StringBuffer sb  = newStringBuffer("");

       for(Iterator<String> urls =  restfulUrls.iterator(); urls.hasNext(); ) {

           String url = urls.next();

           if(! url.startsWith("/")) {

              url = "/"+ url ;

           }

           sb.append(url).append("=").append("authc, rest[").append(url).append("]").append(CRLF);

       }

       returnsb.toString();

       

       

    }

    

    

    //根據角色,得到動態權限規則

    privateString getDynaAuthRule() {

       

       StringBuffer sb = newStringBuffer("");

       Map<String, Set<String>> rules = newHashMap<String,Set<String>>();

       

       List<Role> roles = dao.queryEntitys("from Role r left join fetch r.menus"newObject[]{});

       for(Role role: roles) {

           for(Iterator<Menu> menus =role.getMenus().iterator(); menus.hasNext();) {

              String url = menus.next().getUrl();

              if(!url.startsWith("/")) {

                  url = "/"+ url;

              }

              if(!rules.containsKey(url)) {

                  rules.put(url, newHashSet<String>());

              }

              rules.get(url).add((role.getRoleCode()));

           }

       }

       

       for(Map.Entry<String, Set<String>> entry :rules.entrySet()) {

           sb.append(entry.getKey()).append(" = ").append("authc,roleOrFilter").append(entry.getValue()).append(CRLF);

       }

       

       returnsb.toString();

    }

    

    

    //得到固定權限驗證規則串

    privateString getFixedAuthRule() {

       

       StringBuffer sb = newStringBuffer("");

       

       ClassPathResource cp = newClassPathResource("fixed_auth_res.properties");

       Properties properties = newOrderedProperties();

       try{

           properties.load(cp.getInputStream());

       } catch(IOException e) {

           log.error("loadfixed_auth_res.properties error!", e);

           throw newRuntimeException("load fixed_auth_res.properties error!");

       }

       for(Iteratorits = properties.keySet().iterator();its.hasNext();) {

           String key = (String)its.next();

           sb.append(key).append(" = ").append(properties.getProperty(key).trim()).append(CRLF);

           

       }      

       returnsb.toString();

       

    }

    

    @Override

    //此方法加同步鎖

    public synchronized voidreCreateFilterChains() {

       

       AbstractShiroFilter shiroFilter = null;

       try{

           shiroFilter = (AbstractShiroFilter)shiroFilterFactoryBean.getObject();

       } catch(Exception e) {

           log.error("getShiroFilter from shiroFilterFactoryBean error!", e);

           throw newRuntimeException("get ShiroFilter from shiroFilterFactoryBean error!");

       }

       

       PathMatchingFilterChainResolver filterChainResolver =(PathMatchingFilterChainResolver)shiroFilter.getFilterChainResolver();

       DefaultFilterChainManager manager =(DefaultFilterChainManager)filterChainResolver.getFilterChainManager();

 

       //清空老的權限控制

       manager.getFilterChains().clear();

       

 

       shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();

       shiroFilterFactoryBean.setFilterChainDefinitions(loadFilterChainDefinitions());

       //重新構建生成

       Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();

        for(Map.Entry<String, String> entry :chains.entrySet()) {

            String url = entry.getKey();

            String chainDefinition =entry.getValue().trim().replace(" """);

            manager.createChain(url,chainDefinition);

        }

       

    }

 

}

在項目中集成shiro權限框架(4)

在實現loadFilterChainDefinitions方法時,我把權限規則分成了三個部分,一個是固定規則,比如哪些不需要過渡等,另一部分是菜單級動態規則,最後一部分是基於restful的功能規則(當然,如果系統不是基於restful的,需要根據實際情況修改)。

固定規則保存在一個叫fixed_auth_res.properties的文件中,內容大致是這樣的:

#fixed authrules

/js/**= anon

/css/**= anon

              

/login.jsp = anon

/login = anon

              

 

下面是DbAuthRealm類,按照我們的規則,從數據庫中取出信息,對用戶進行驗證,密碼的加密算法也是在些檢測。

/**

 * 數據庫存儲認證類信息

 * @authorljh

 *

 */

public classDbAuthRealm extendsAuthorizingRealm {

 

    @Resource(name = "studentService")

    privateIStudentService studentService;

 

    publicDbAuthRealm() {

       super();

       // 設置認證token的實現類爲用戶名密碼模式

       this.setAuthenticationTokenClass(UsernamePasswordToken.class);

       //設置驗證方式,用戶自行設定密碼加密方式

       this.setCredentialsMatcher(newCredentialsMatcher() {   

           @Override

           public booleandoCredentialsMatch(AuthenticationToken token,AuthenticationInfo info) {

              

              UsernamePasswordToken upToken = (UsernamePasswordToken)token;

              String pwd = newString(upToken.getPassword());

              

              User student = studentService.getStudentById(Long.parseLong(upToken.getUsername()));

 

              if(student.getPassword().equals(DigestUtils.md5Hex(pwd))){

                  //用戶名及密碼驗證通過

                  return true;

              }

              //用戶名或密碼不正確

              return false;

           }

       });

    }

 

    // 認證

    @Override

    protectedAuthenticationInfo doGetAuthenticationInfo(

           AuthenticationToken token) throwsAuthenticationException {

       

 

       UsernamePasswordToken upToken = (UsernamePasswordToken) token;

 

       //調用業務方法

       User student = studentService.getStudentById(Long.parseLong(upToken.getUsername()));

 

       if(student != null) {

           //要放在作用域中的東西,請在這裏進行操作

           SecurityUtils.getSubject().getSession().setAttribute("c_user", student);

           

           return newSimpleAuthenticationInfo(student.getId(),student.getPassword(), this.getName());

       }

       //認證沒有通過

       return null;

    }

 

    // 授權

    @Override

    protectedAuthorizationInfo doGetAuthorizationInfo(

           PrincipalCollection principalCollection) {

 

       Long loginId = (Long)principalCollection.fromRealm(getName()).iterator().next();

       //取當前用戶

       User student = studentService.getStudentById(loginId);

       //添加角色及權限信息

       SimpleAuthorizationInfo sazi = newSimpleAuthorizationInfo();

       sazi.addRoles(student.getRolesAsString());

       sazi.addStringPermissions(student.getPermissionsAsString());

 

       returnsazi;

    }

 

    @Override

    protected voidclearCachedAuthorizationInfo(PrincipalCollection pc) {

       SimplePrincipalCollection principalsnewSimplePrincipalCollection(pc, getName()); 

       super.clearCachedAuthorizationInfo(pc);

    }

 

 

最後,來看看入口控制器關於登錄、退出的代碼:

@Controller

public classMyAction {

    

    @Resource

    privateShiroFilterFactoryBean shiroFilterFactoryBean;

    @Resource

    privateIAuthService authService;

    

    @RequestMapping(value="/login")

    publicString login(

           @RequestParam(value="username", defaultValue="") String username,

           @RequestParam(value="password", defaultValue="") String password,

           HttpServletRequest req) {

       

       Subject currentUser = SecurityUtils.getSubject();

       

       booleanloginSuccess = false;

 

       //用戶已經登錄過了,直接進主頁面

       if(currentUser.isAuthenticated()) {

           loginSuccess = true;

       } 

       

       try{

           currentUser.login(newUsernamePasswordToken(username, password));

           loginSuccess = true;

       } catch(Exception e) {

           e.printStackTrace();

           req.setAttribute("error_info""用戶名或密碼錯,請覈對!");

           return "login";

       }

       

       if(loginSuccess) {

           //生成用戶菜單(當然,也可以在main中完成,但在重定向的情況下會有hibernatesession延遲問題)

           User s = (User)currentUser.getSession().getAttribute("c_user");

           //currentUser.getSession().setAttribute("menus",s.getMenus());

           currentUser.getSession().setAttribute("models", s.getModels());

           return "redirect:main";

       }

       return "login";

       

    }

    

    @RequestMapping(value="/logout")

    publicString logout() {

       

       Subject currentUser = SecurityUtils.getSubject();

       currentUser.logout();

       

       return "login";

       

       

    }

    

    @RequestMapping(value="/main")

    publicString index() {

 

       User s = (User)SecurityUtils.getSubject().getSession().getAttribute("c_user");

       

       System.out.println(s.getName() + "進入主頁面!");

 

       return "main_page";

       

       

    }

 

最後,RolesOrFilter代碼貼出:

/**

 * 在進行角色檢測時,只要有一個滿足即通

 * @authorljh

 *

 */

public classRolesOrFilter extendsAuthorizationFilter {

 

    

    @SuppressWarnings({"unchecked"})

    public booleanisAccessAllowed(ServletRequestrequest, ServletResponse response, Object mappedValue) throwsIOException {

 

        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;

        }

        

        for(String role : rolesArray) {

         if(subject.hasRole(role)) {

             return true;

         }

        }

 

        return false;

        

    }

    

}

 

最後一個輔助類,在讀取properties時,按key/val在源文件中的順序讀出:

/**

 * 有序properties

 * @authorljh

 */

public classOrderedProperties extendsProperties {

 

    private static final long serialVersionUID= -4827607240846121968L;

    

    private finalLinkedHashSet<Object> keysnewLinkedHashSet<Object>();

 

    publicEnumeration<Object>keys() {

        returnCollections.<Object> enumeration(keys);

    }

 

    publicObject put(Object key, Objectvalue) {

        keys.add(key);

        return super.put(key, value);

    }

 

    publicSet<Object> keySet() {

        return keys;

    }

 

    publicSet<String>stringPropertyNames() {

        Set<String> set = newLinkedHashSet<String>();

 

        for(Object key : this.keys) {

            set.add((String) key);

        }

 

        returnset;

    }

}

 

好了,shiro的簡單的權限整合到此結束,上面的代碼實現了用戶功能菜單根據權限動態生成,基於url的訪問控制,基於restful的訪問控制,權限粒度到了功能按鈕級別。

另外,如果你要實現基於方法級的權限,shiro也是支持的,它提供了相應的註解【當然你也可以用AOP來自己寫,需要用到threadlocal對象傳遞一些信息哦】。如果你想實現基於數據級的權限,我想最好的辦法還是自己在業務實現中進行處理吧。



如果你想學習Java工程化、高性能及分佈式、高性能、深入淺出。性能調優、Spring,MyBatis,Netty源碼分析和大數據等知識點可以來找我。


而現在我就有一個平臺可以提供給你們學習,讓你在實踐中積累經驗掌握原理。主要方向是JAVA架構師。如果你想拿高薪,想突破瓶頸,想跟別人競爭能取得優勢的,想進BAT但是有擔心面試不過的,可以加我的Java架構進階羣:554355695


注:加羣要求


1、具有2-5工作經驗的,面對目前流行的技術不知從何下手,需要突破技術瓶頸的可以加。 
2、在公司待久了,過得很安逸,但跳槽時面試碰壁。需要在短時間內進修、跳槽拿高薪的可以加。 
3、如果沒有工作經驗,但基礎非常紮實,對java工作機制,常用設計思想,常用java開發框架掌握熟練的,可以加。 
4、覺得自己很牛B,一般需求都能搞定。但是所學的知識點沒有系統化,很難在技術領域繼續突破的可以加。 
5.阿里Java高級大牛直播講解知識點,分享知識,多年工作經驗的梳理和總結,帶着大家全面、科學地建立自己的技術體系和技術認知! 
6.小號加羣一律不給過,謝謝。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章