在項目中集成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> roles= newHashSet<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> menus= newHashSet<Menu>();
//角色可操作的操作功能【對應於增、刪、查、改等功能】
privateSet<Operation> operations= newHashSet<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> menus= newHashSet<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.jsp= authc,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 principals= newSimplePrincipalCollection(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> keys= newLinkedHashSet<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.小號加羣一律不給過,謝謝。