日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

shiro 方法级别细粒度权限控制_Shiro的认证和权限控制

發(fā)布時(shí)間:2025/3/15 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 shiro 方法级别细粒度权限控制_Shiro的认证和权限控制 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

從類別上分,有兩大類:

- 認(rèn)證:你是誰?–識(shí)別用戶身份。

- 授權(quán):你能做什么?–限制用戶使用的功能。

權(quán)限的控制級(jí)別

從控制級(jí)別(模型)上分:

- URL級(jí)別-粗粒度

- 方法級(jí)別-細(xì)粒度

- 頁面級(jí)別-自定義標(biāo)簽(顯示)

- 數(shù)據(jù)級(jí)別-最細(xì)化的(數(shù)據(jù))

URL級(jí)別的權(quán)限控制-粗粒度

在web.xml中配置一個(gè)過濾器filter,在過濾器中,對(duì)請(qǐng)求的地址進(jìn)行解析,字符串截取:

url.substring()…把上下文前面的路徑都截取掉,剩下user_login.action。

過濾器代碼:

以通過查詢數(shù)據(jù)庫,來判斷,當(dāng)前登錄用戶,是否可以訪問user_login.action。

url級(jí)別控制,每次請(qǐng)求過程中只控制一次 ,相比方法級(jí)別權(quán)限控制 是粗粒度的 !URL級(jí)別權(quán)限控制,基于Filter實(shí)現(xiàn)。

方法級(jí)別的權(quán)限控制-細(xì)粒度

aop面向切面的編程,在方法執(zhí)行之前,進(jìn)行權(quán)限判斷,如果沒有權(quán)限,拋出異常,終止方法的繼續(xù)運(yùn)行。

自定義注解 在需要權(quán)限控制方法上, 添加需要的權(quán)限信息

代理 (Spring AOP ),在目標(biāo)方法運(yùn)行時(shí) 進(jìn)行增強(qiáng) ,通過反射技術(shù)獲取目標(biāo)方法上注解中權(quán)限 , 查詢數(shù)據(jù)庫獲取當(dāng)前登陸用戶具有權(quán)限,進(jìn)行比較。

相比URL級(jí)別權(quán)限控制, 可以控制到服務(wù)器端執(zhí)行的每個(gè)方法,一次請(qǐng)求中可以控制多次。

頁面(顯示)級(jí)別的權(quán)限控制-自定義標(biāo)簽

頁面顯示的權(quán)限控制,通常是通過 自定義標(biāo)簽來實(shí)現(xiàn)

數(shù)據(jù)級(jí)別的權(quán)限控制

在每條數(shù)據(jù)上增加一個(gè)字段,該字段記錄了權(quán)限的值。數(shù)據(jù)和權(quán)限綁定。

代碼,你在查詢數(shù)據(jù)的時(shí)候,需要去權(quán)限和用戶對(duì)應(yīng)表中,通過當(dāng)前登錄用戶的條件,查詢出你的數(shù)據(jù)權(quán)限。然后再將數(shù)據(jù)權(quán)限作為一個(gè)條件,放到業(yè)務(wù)表中進(jìn)行查詢。從而限制了數(shù)據(jù)的訪問。

權(quán)限系統(tǒng)的數(shù)據(jù)表設(shè)計(jì)

資源:用戶要訪問的目標(biāo),通常是服務(wù)中的程序或文件

權(quán)限:用戶具有訪問某資源的能力

角色:權(quán)限的集合,為了方便給用戶授權(quán)。

用戶:訪問系統(tǒng)的’人’。

表對(duì)象實(shí)體:

- 用戶(User)表:訪問系統(tǒng)的用戶,比如用戶登錄要用

- 權(quán)限(Function)表:系統(tǒng)某個(gè)功能允許訪問而對(duì)應(yīng)的權(quán)限

- 角色(Role)表:角色是權(quán)限的集合(權(quán)限組),方便用戶授權(quán)。

表對(duì)象之間的關(guān)系:

- 用戶和角色關(guān)系表:一個(gè)用戶對(duì)應(yīng)N個(gè)角色,一個(gè)角色可以授予N個(gè)用戶—》多對(duì)多關(guān)系

- 角色和權(quán)限關(guān)系表:一個(gè)角色包含N個(gè)權(quán)限,一個(gè)權(quán)限可以屬于N個(gè)角色—》多對(duì)多關(guān)系

完整的權(quán)限相關(guān)表:

URL級(jí)別權(quán)限控制包含:資源表、權(quán)限表、角色表、用戶表,以及相關(guān)關(guān)系(都是多對(duì)多),共7張表。

方法級(jí)別的權(quán)限控制包含:功能權(quán)限、角色、用戶,以及相關(guān)關(guān)系(都是多對(duì)多),共5張表。

但Apache Shiro框架支持的URL級(jí)別權(quán)限控制,是將資源和資源權(quán)限對(duì)應(yīng)關(guān)系配置到了配置文件中,不需要表的支撐,只需要5張表了。

Apache Shiro權(quán)限控制

Apache Shiro 可以不依賴任何技術(shù)使用, 可以直接和web整合,通常在企業(yè)中和Spring 結(jié)合使用。

Authentication: 認(rèn)證 — 用戶登錄

Authorization : 授權(quán) —- 功能權(quán)限管理

通過引入Maven坐標(biāo)導(dǎo)入shiro

官方建議:不推薦直接引入shiro-all,依賴比較多,原因怕有jar沖突。官方推薦根據(jù)需要單獨(dú)導(dǎo)入jar。

Shiro基本原理

Shiro的框架的體系結(jié)構(gòu):

Shiro權(quán)限控制流程的原理:

應(yīng)用代碼 —- 調(diào)用Subject (shiro的Subject 就代表當(dāng)前登陸用戶) 控制權(quán)限 —- Subject 在shiro框架內(nèi)部 調(diào)用 Shiro SecurityManager 安全管理器 —– 安全管理器調(diào)用 Realm (程序和安全數(shù)據(jù)連接器 )。

Subject要進(jìn)行任何操作,都必須要調(diào)用安全管理器(對(duì)我們來說是自動(dòng)的)。

而安全管理器會(huì)調(diào)用指定的Realms對(duì)象,來連接安全數(shù)據(jù)。

Realms用來編寫安全代碼邏輯和訪問安全數(shù)據(jù),是連接程序和安全數(shù)據(jù)的橋梁。

URL級(jí)別的權(quán)限控制

配置整合和url級(jí)別認(rèn)證

配置過濾器web.xml:放在struts的前端控制器之前配置,但放在openEntitymanage之后。

shiroSecurityFilter

org.springframework.web.filter.DelegatingFilterProxy

targetFilterLifecycle

true

shiroSecurityFilter

/*

配置ApplicationContext.xml:(shiro權(quán)限控制過濾器+ shiro安全管理器)

/login.jsp = anon

/validatecode.jsp = anon

/js/** = anon

/css/** = anon

/images/** = anon

/user_login.action* = anon

/page_base_staff.action = anon

/page_base_region.action = perms["user"]

/page_base_subarea.action = roles["operator"]

/** = authc

配置shiroFilter 其實(shí)是一個(gè)過濾器鏈,含有10個(gè)Filter(校驗(yàn)功能)。

常用:

認(rèn)證

- anon不用認(rèn)證(登錄)就能訪問(單詞注意大小寫)

- authc: 需要認(rèn)證(登錄)才能使用,例如/admins/user/**=authc,沒有參數(shù)。

授權(quán):

- perms:需要擁有某權(quán)限才能使用,如具體允許的權(quán)限:/page_base_region.action =perms[“user”],如果要訪問該action,當(dāng)前登錄用戶必須擁有user名字的權(quán)限。

- roles:需要擁有某角色才能使用,如具體允許的角色:/page_base_subarea.action = roles[“operator”]如果要訪問該action,當(dāng)前用戶必須擁有operator權(quán)限。

用戶認(rèn)證(登錄)—自定義Realm

Shiro實(shí)現(xiàn)登錄邏輯

用戶輸入用戶名和密碼 —- 應(yīng)用程序調(diào)用Subject的login方法 —- Subject 調(diào)用SecurityManager的方法 —- SecurityManager 調(diào)用Realm的認(rèn)證方法 —- 認(rèn)證方法根據(jù)登錄用戶名查詢密碼 ,返回用戶的密碼 —- SecurityManager 比較用戶輸入的密碼和真實(shí)密碼是否一致 。

編寫Shiro的認(rèn)證登錄邏輯

@Action(value="user_login",results={@Result(name=SUCCESS,type="redirect",location="/index.jsp"),@Result(name=LOGIN,location="/login.jsp")})

@InputConfig(resultName="login")

public String login() throws Exception {

//shrio:登陸邏輯

//獲取認(rèn)證對(duì)象的包裝對(duì)象

Subject subject = SecurityUtils.getSubject();

//獲取一個(gè)認(rèn)證的令牌:

//直接獲取頁面的用戶和密碼進(jìn)行校驗(yàn)

AuthenticationToken authenticationToken = new UsernamePasswordToken(model.getUsername(),MD5Utils.md5(model.getPassword()));

//認(rèn)證過程

try {

// 如果成功,就不拋出異常,會(huì)自動(dòng)將用戶放入session的一個(gè)屬性

subject.login(authenticationToken);

//成功,返回首頁

return SUCCESS;

}catch(UnknownAccountException e){

//用戶名錯(cuò)誤

addActionError(getText("UserAction.usernamenotfound"));

//返回登陸頁面

return LOGIN;

}catch (IncorrectCredentialsException e) {

//密碼錯(cuò)誤

addActionError(getText("UserAction.passwordinvalid"));

//返回登陸頁面

return LOGIN;

}

catch (AuthenticationException e) {

//認(rèn)證失敗

e.printStackTrace();

//頁面上進(jìn)行提示

addActionError(getText("UserAction.loginfail"));

//返回登陸頁面

return LOGIN;

}

}

編寫Realm,給SecurityManager提供

JdbcRealm和jndiLdapRealm,直接連接jdbc或jndi或ldap。

相當(dāng)于dao和reaml整合了,能直接讀取數(shù)據(jù)庫,邏輯代碼都實(shí)現(xiàn)好了。

/**

* 實(shí)現(xiàn)認(rèn)證和授權(quán)功能

*自定義的realm,作用從數(shù)據(jù)庫查詢數(shù)據(jù),并返回?cái)?shù)據(jù)庫認(rèn)證的信息

*/

@Component("bosRealm")

public class BosRealm extends AuthorizingRealm{

//注入ehcache的緩存區(qū)域

@Value("BosShiroCache")//注入緩存具體對(duì)象的名字,該名字在ehcache.xml中配置的

public void setSuperAuthenticationCacheName(String authenticationCacheName){

super.setAuthenticationCacheName(authenticationCacheName);

}

//注入service

@Autowired

private UserService userService;

//注入角色dao

@Autowired

private RoleDao roleDao;

//注入功能的dao

@Autowired

private FunctionDao functionDao;

//授權(quán)方法:獲取用戶的權(quán)限信息

//授權(quán):回調(diào)方法

//如果返回null,說明沒有權(quán)限,shiro會(huì)自動(dòng)跳到

//如果不返回null,根據(jù)配置/page_base_subarea.action = roles["weihu"],去自動(dòng)匹配

//給授權(quán)提供數(shù)據(jù)的

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

//給當(dāng)前用戶授權(quán)的權(quán)限(功能權(quán)限、角色)

SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

//兩種方式:

//方式1:工具類來獲取(首長-)

// User user=(User)SecurityUtils.getSubject().getPrincipal();

//方式2:通過參數(shù)獲取首長(推薦)

User user = (User) principals.getPrimaryPrincipal();

//實(shí)際:需要根據(jù)當(dāng)前用戶的角色和功能權(quán)限來構(gòu)建一個(gè)授權(quán)信息對(duì)象,交給安全管理器

if (user.getUsername().equals("admin")) {

//如果是超級(jí)管理員

//查詢出所有的角色,給認(rèn)證信息對(duì)象

List roleList = roleDao.findAll();

for (Role role : roleList) {

authorizationInfo.addRole(role.getCode());

}

//查詢出所有的功能權(quán)限,給認(rèn)證對(duì)象

List functionList = functionDao.findAll();

for (Function function : functionList) {

authorizationInfo.addStringPermission(function.getCode());

}

} else {

//如果是普通用戶

List roleList = roleDao.findByUsers(user);

for (Role role : roleList) {

authorizationInfo.addRole(role.getCode());

//導(dǎo)航查詢,獲取某角色的擁有的功能權(quán)限

Set functions = role.getFunctions();

for (Function function : functions) {

authorizationInfo.addStringPermission(function.getCode());

}

}

}

return authorizationInfo;//將授權(quán)信息交給安全管理器接口。

}

//認(rèn)證:回調(diào),認(rèn)證管理器會(huì)將認(rèn)證令牌放到這里(action層的令牌AuthenticationToken)

//發(fā)現(xiàn)如果返回null,拋出用戶不存在的異常UnknownAccountException

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

//用戶名密碼令牌(action傳過來)

UsernamePasswordToken upToken = (UsernamePasswordToken) token;

//調(diào)用業(yè)務(wù)層來查詢(根據(jù)用戶名來查詢用戶,無需密碼)

User user = userService.findByUsername(upToken.getUsername());

//判斷用戶是否存在

if (user == null) {

//用戶不存在

return null;//拋出異常

} else {

//用戶名存在

//參數(shù)1:用戶對(duì)象,將來要放入session,數(shù)據(jù)庫查詢出來的用戶

//參數(shù)2:憑證(密碼):密碼校驗(yàn):校驗(yàn)的動(dòng)作交給shiro

//參數(shù)3:當(dāng)前使用的Realm在Spring容器中的名字(bean的名字,自動(dòng)在spring容器中尋找)

SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), super.getName());

return authenticationInfo;//密碼校驗(yàn)失敗,會(huì)自動(dòng)拋出IncorrectCredentialsException

}

}

}

ApplicatonContext.xml:

用戶認(rèn)證(退出)以及修改密碼

/**

* 用戶退出登錄

*@return

*@throws Exception

*/

@Action(value="user_logout",results={@Result(name=LOGIN,type="redirect",location="/login.jsp")})

public String logout() throws Exception {

//shiro退出

Subject subject = SecurityUtils.getSubject();

subject.logout();

//跳轉(zhuǎn)登陸頁面

return LOGIN;

}

/**

* 用戶修改密碼

*@return

*@throws Exception

*/

// @Action(value="user_editPassword",results={@Result(name=JSON,type=JSON)})

@Action("user_editPassword")

public String editPassword() throws Exception {

//獲取Principal就是獲取當(dāng)前用戶

User loginUser = (User) SecurityUtils.getSubject().getPrincipal();

model.setId(loginUser.getId());

//頁面結(jié)果

HashMap resultMap = new HashMap();

try {

//調(diào)用service進(jìn)行修改密碼

userService.updateUserPassword(model);

//修改成功

resultMap.put("result", true);

} catch (Exception e) {

e.printStackTrace();

//修改失敗

resultMap.put("result", false);

}

//將結(jié)果壓入棧頂

ActionContext.getContext().getValueStack().push(resultMap);

//轉(zhuǎn)換為json

return JSON;

}

用戶授權(quán)(授權(quán))—自定義Ream

數(shù)據(jù)庫數(shù)據(jù)添加,applicationContext.xml配置

/login.jsp = anon

/validatecode.jsp = anon

/js/** = anon

/css/** = anon

/images/** = anon

/user_login.action = anon

/page_base_staff.action = anon

/page_base_region.action = perms["region"]

/page_base_subarea.action = roles["weihu"]

/page_qupai_noticebill_add.action = perms["noticebill"]

/page_qupai_quickworkorder.action = roles["kefu"]

/** = authc

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

代碼在上面的BosRealm的中,protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)

RoleDao省略。

方法級(jí)別的權(quán)限控制

啟用Shiro注解

需要 Shiro 的 Spring AOP 集成來掃描合適的注解類以及執(zhí)行必要的安全邏輯。

ApplicationContext.xml

depends-on="lifecycleBeanPostProcessor">

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

在需要權(quán)限控制的目標(biāo)方法上面使用shiro的注解:

@RequiresAuthentication 需要用戶登錄

subject.isAuthenticated() 必須返回true

@ RequiresUser

subject.isAuthenticated() 返回true 或者subject.isRemembered() 返回true

“Remember Me”服務(wù):

認(rèn)證機(jī)制 基于 session

被記憶機(jī)制 基于 cookie (subject.isAuthenticated() 返回 false )

@ RequiresGuest 與 @RequiresUser 相反,不能認(rèn)證也不能被記憶。

@ RequiresRoles 需要角色

@RequiresPermissions 需要權(quán)限

異常

動(dòng)態(tài)代理異常

解決方案:

配置ApplicationContext.xml,設(shè)置代理為cglib代理(對(duì)目標(biāo)類代理)

depends-on="lifecycleBeanPostProcessor" >

1

2

3

4

5

6

方案二:

1

類型轉(zhuǎn)換異常

解決方案:遞歸向上尋找泛型的類型。

//遞歸向上 查找

Class actionClass =this.getClass();

//向父類遞歸尋找泛型

while(true){

//得到帶有泛型的類型,如BaseAction

Type type = actionClass.getGenericSuperclass();

if(type instanceof ParameterizedType){

//轉(zhuǎn)換為參數(shù)化類型

ParameterizedType parameterizedType = (ParameterizedType) type;

//獲取泛型的第一個(gè)參數(shù)的類型類,如Userinfo

Class modelClass = (Class) parameterizedType.getActualTypeArguments()[0];

//實(shí)例化模型對(duì)象

try {

model=modelClass.newInstance();

} catch (InstantiationException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}

break;

}

//尋找父類

actionClass=actionClass.getSuperclass();

}

空指針異常

解決方案1:使用public 的Setter方法上的注解直接注入Service。

SubareaAction:

//注入service

private SubareaService subareaService;

@Autowired

public void setSubareaService(SubareaService subareaService) {

this.subareaService = subareaService;

}

解決方案2:

@Autowire還放到私有聲明上,

在struts.xml中覆蓋常量(開啟自動(dòng)裝配策略):

值默認(rèn)是false,struts2默認(rèn)注入采用的是構(gòu)造器注入(從spring中尋找的bean)

改成true,struts2會(huì)采用setter方法注入

頁面標(biāo)簽(實(shí)現(xiàn)頁面內(nèi)容定制顯示)

頁面拿Session中的user對(duì)象: 代表user對(duì)象。

程序中拿Session中的user對(duì)象:SecurityUtils.getSubject().getPrincipal()

資源通配符和權(quán)限通配符可便捷開發(fā)。

代碼級(jí)別

使用代碼編程的方式,直接在程序中使用Subject對(duì)象,調(diào)用內(nèi)部的一些API。(有代碼侵入)

//代碼級(jí)別的權(quán)限控制(授權(quán)):功能權(quán)限和角色權(quán)限:兩套機(jī)制:boolean判斷,異常判斷

//授權(quán)的權(quán)限控制

//====布爾值判斷

//功能權(quán)限

if(subject.isPermitted("staff")){

//必須擁有staff功能權(quán)限才能執(zhí)行代碼

System.out.println("我是一段代碼。。。。。");

}

//角色權(quán)限

if(subject.hasRole("weihu")){

//必須擁有staff功能權(quán)限才能執(zhí)行代碼

System.out.println("我是一段代碼。。。。。");

}

//====異常判斷

//功能權(quán)限

try {

subject.checkPermission("staff");

//有權(quán)限

} catch (AuthorizationException e) {

// 沒權(quán)限

e.printStackTrace();

}

//角色權(quán)限

try {

subject.checkRole("weihu");

//有權(quán)限

} catch (AuthorizationException e) {

// 沒權(quán)限

e.printStackTrace();

}

總結(jié)

以上是生活随笔為你收集整理的shiro 方法级别细粒度权限控制_Shiro的认证和权限控制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。