激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - spring boot 集成 shiro 自定義密碼驗證 自定義freemarker標簽根據權限渲染不同頁面(推薦)

spring boot 集成 shiro 自定義密碼驗證 自定義freemarker標簽根據權限渲染不同頁面(推薦)

2021-06-19 10:30朋也 Java教程

這篇文章主要介紹了spring-boot 集成 shiro 自定義密碼驗證 自定義freemarker標簽根據權限渲染不同頁面,需要的朋友可以參考下

項目里一直用的是 spring-security ,不得不說,spring-security 真是東西太多了,學習難度太大(可能我比較菜),這篇博客來總結一下折騰shiro的成果,分享給大家,強烈推薦shiro,真心簡單 : )

引入依賴

?
1
2
3
4
5
<dependency>
 <groupid>org.apache.shiro</groupid>
 <artifactid>shiro-spring</artifactid>
 <version>1.4.0</version>
</dependency>

用戶,角色,權限

就是經典的rbac權限系統,下面簡單給一下實體類字段

adminuser.java

?
1
2
3
4
5
6
7
8
9
public class adminuser implements serializable {
 
 private static final long serialversionuid = 8264158018518861440l;
 private integer id;
 private string username;
 private string password;
 private integer roleid;
 // getter setter...
}

role.java

?
1
2
3
4
5
6
public class role implements serializable {
 private static final long serialversionuid = 7824693669858106664l;
 private integer id;
 private string name;
 // getter setter...
}

permission.java

?
1
2
3
4
5
6
7
8
9
public class permission implements serializable {
 private static final long serialversionuid = -2694960432845360318l;
 private integer id;
 private string name;
 private string value;
 // 權限的父節點的id
 private integer pid;
 // getter setter...
}

自定義realm

這貨就是查詢用戶的信息然后放在shiro的個人用戶對象的緩存里,shiro自己有一個session的對象(不是servlet里的session)作用就是后面用戶發起請求的時候拿來判斷有沒有權限

另一個作用是查詢一下用戶的信息,將用戶名,密碼組裝成一個 authenticationinfo 用于后面密碼校驗的

具體代碼如下

myshirorealm.java

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@component
public class myshirorealm extends authorizingrealm {
 private logger log = loggerfactory.getlogger(myshirorealm.class);
 @autowired
 private adminuserservice adminuserservice;
 @autowired
 private roleservice roleservice;
 @autowired
 private permissionservice permissionservice;
 // 用戶權限配置
 @override
 protected authorizationinfo dogetauthorizationinfo(principalcollection principals) {
 //訪問@requirepermission注解的url時觸發
 simpleauthorizationinfo simpleauthorizationinfo = new simpleauthorizationinfo();
 adminuser adminuser = adminuserservice.selectbyusername(principals.tostring());
 //獲得用戶的角色,及權限進行綁定
 role role = roleservice.selectbyid(adminuser.getroleid());
 // 其實這里也可以不要權限那個類了,直接用角色這個類來做鑒權,
 // 不過角色包含很多的權限,已經算是大家約定的了,所以下面還是查詢權限然后放在authorizationinfo里
 simpleauthorizationinfo.addrole(role.getname());
 // 查詢權限
 list<permission> permissions = permissionservice.selectbyroleid(adminuser.getroleid());
 // 將權限具體值取出來組裝成一個權限string的集合
 list<string> permissionvalues = permissions.stream().map(permission::getvalue).collect(collectors.tolist());
 // 將權限的string集合添加進authorizationinfo里,后面請求鑒權有用
 simpleauthorizationinfo.addstringpermissions(permissionvalues);
 return simpleauthorizationinfo;
 }
 // 組裝用戶信息
 @override
 protected authenticationinfo dogetauthenticationinfo(authenticationtoken token) throws authenticationexception {
 string username = (string) token.getprincipal();
 log.info("用戶:{} 正在登錄...", username);
 adminuser adminuser = adminuserservice.selectbyusername(username);
 // 如果用戶不存在,則拋出未知用戶的異常
 if (adminuser == null) throw new unknownaccountexception();
 return new simpleauthenticationinfo(username, adminuser.getpassword(), getname());
 }
}

實現密碼校驗

shiro內置了幾個密碼校驗的類,有 md5credentialsmatcher sha1credentialsmatcher , 不過從1.1版本開始,都開始使用 hashedcredentialsmatcher 這個類了,通過配置加密規則來校驗

它們都實現了一個接口 credentialsmatcher 我這里也實現這個接口,實現一個自己的密碼校驗

說明一下,我這里用的加密方式是spring-security里的 bcryptpasswordencoder 作的加密,之所以用它,是因為同一個密碼被這貨加密后,密文都不一樣,下面是具體代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class mycredentialsmatcher implements credentialsmatcher {
 
 @override
 public boolean docredentialsmatch(authenticationtoken token, authenticationinfo info) {
 // 大坑!!!!!!!!!!!!!!!!!!!
 // 明明token跟info兩個對象的里的credentials類型都是object,斷點看到的類型都是 char[]
 // 但是!!!!! token里轉成string要先強轉成 char[]
 // 而info里取credentials就可以直接使用 string.valueof() 轉成string
 // 醉了。。
 string rawpassword = string.valueof((char[]) token.getcredentials());
 string encodedpassword = string.valueof(info.getcredentials());
 return new bcryptpasswordencoder().matches(rawpassword, encodedpassword);
 }
}

配置shiro

因為項目是spring-boot開發的,shiro就用java代碼配置,不用xml配置, 具體配置如下

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@configuration
public class shiroconfig {
 private logger log = loggerfactory.getlogger(shiroconfig.class);
 @autowired
 private myshirorealm myshirorealm;
 @bean
 public shirofilterfactorybean shirofilter(securitymanager securitymanager) {
 log.info("開始配置shirofilter...");
 shirofilterfactorybean shirofilterfactorybean = new shirofilterfactorybean();
 shirofilterfactorybean.setsecuritymanager(securitymanager);
 //攔截器.
 map<string,string> map = new hashmap<>();
 // 配置不會被攔截的鏈接 順序判斷 相關靜態資源
 map.put("/static/**", "anon");
 //配置退出 過濾器,其中的具體的退出代碼shiro已經替我們實現了
 map.put("/admin/logout", "logout");
 //<!-- 過濾鏈定義,從上向下順序執行,一般將/**放在最為下邊 -->:這是一個坑呢,一不小心代碼就不好使了;
 //<!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問-->
 map.put("/admin/**", "authc");
 // 如果不設置默認會自動尋找web工程根目錄下的"/login.jsp"頁面
 shirofilterfactorybean.setloginurl("/adminlogin");
 // 登錄成功后要跳轉的鏈接
 shirofilterfactorybean.setsuccessurl("/admin/index");
 //未授權界面;
 shirofilterfactorybean.setunauthorizedurl("/error");
 shirofilterfactorybean.setfilterchaindefinitionmap(map);
 return shirofilterfactorybean;
 }
 // 配置加密方式
 // 配置了一下,這貨就是驗證不過,,改成手動驗證算了,以后換加密方式也方便
 @bean
 public mycredentialsmatcher mycredentialsmatcher() {
 return new mycredentialsmatcher();
 }
 // 安全管理器配置
 @bean
 public securitymanager securitymanager() {
 defaultwebsecuritymanager securitymanager = new defaultwebsecuritymanager();
 myshirorealm.setcredentialsmatcher(mycredentialsmatcher());
 securitymanager.setrealm(myshirorealm);
 return securitymanager;
 }
}

登錄

都配置好了,就可以發起登錄請求做測試了,一個簡單的表單即可,寫在controller里就行

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@postmapping("/adminlogin")
public string adminlogin(string username, string password,
       @requestparam(defaultvalue = "0") boolean rememberme,
       redirectattributes redirectattributes) {
 try {
 // 添加用戶認證信息
 subject subject = securityutils.getsubject();
 if (!subject.isauthenticated()) {
  usernamepasswordtoken token = new usernamepasswordtoken(username, password, rememberme);
  //進行驗證,這里可以捕獲異常,然后返回對應信息
  subject.login(token);
 }
 } catch (authenticationexception e) {
 // e.printstacktrace();
 log.error(e.getmessage());
 redirectattributes.addflashattribute("error", "用戶名或密碼錯誤");
 redirectattributes.addflashattribute("username", username);
 return redirect("/adminlogin");
 }
 return redirect("/admin/index");
}

從上面代碼可以看出,記住我功能也直接都實現好了,只需要在組裝 usernamepasswordtoken 的時候,將記住我字段傳進去就可以了,值是 true, false, 如果是true,登錄成功后,shiro會在本地寫一個cookie

調用 subject.login(token); 方法后,它會去鑒權,期間會產生各種各樣的異常,有以下幾種,可以通過捕捉不同的異常然后提示頁面不同的錯誤信息,相當的方便呀,有木有

  • accountexception 帳戶異常
  • concurrentaccessexception 這個好像是并發異常
  • credentialsexception 密碼校驗異常
  • disabledaccountexception 帳戶被禁異常
  • excessiveattemptsexception 嘗試登錄次數過多異常
  • expiredcredentialsexception 認證信息過期異常
  • incorrectcredentialsexception 密碼不正確異常
  • lockedaccountexception 帳戶被鎖定異常
  • unknownaccountexception 未知帳戶異常
  • unsupportedtokenexception login(authenticationtoken) 這個方法只能接收 authenticationtoken 類的對象,如果傳的是其它的類,就拋這個異常

上面這么多異常,shiro在處理登錄的邏輯時,會自動的發出一些異常,當然你也可以手動去處理登錄流程,然后根據不同的問題拋出不同的異常,手動處理的地方就在自己寫的 myshirorealm 里的 dogetauthenticationinfo() 方法里,我在上面代碼里只處理了一個帳戶不存在時拋出了一個 unknownaccountexception 的異常,其實還可以加更多其它的異常,這個要看個人系統的需求來定了

到這里已經可以正常的實現登錄了,下面來說一些其它相關的功能的實現

自定freemarker標簽

開發項目肯定要用到頁面模板,我這里用的是 freemarker ,一個用戶登錄后,頁面可能要根據用戶的不同權限渲染不同的菜單,github上有個開源的庫,也是可以用的,不過我覺得那個太麻煩了,就自己實現了一個,幾行代碼就能搞定

shirotag.java

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@component
public class shirotag {
 // 判斷當前用戶是否已經登錄認證過
 public boolean isauthenticated(){
 return securityutils.getsubject().isauthenticated();
 }
 // 獲取當前用戶的用戶名
 public string getprincipal() {
 return (string) securityutils.getsubject().getprincipal();
 }
 // 判斷用戶是否有 xx 角色
 public boolean hasrole(string name) {
 return securityutils.getsubject().hasrole(name);
 }
 // 判斷用戶是否有 xx 權限
 public boolean haspermission(string name) {
 return !stringutils.isempty(name) && securityutils.getsubject().ispermitted(name);
 }
}

將這個類注冊到freemarker的全局變量里

freemarkerconfig.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@configuration
public class freemarkerconfig {
 private logger log = loggerfactory.getlogger(freemarkerconfig.class);
 @autowired
 private shirotag shirotag;
 @postconstruct
 public void setsharedvariable() throws templatemodelexception {
 //注入全局配置到freemarker
 log.info("開始配置freemarker全局變量...");
 // shiro鑒權
 configuration.setsharedvariable("sec", shirotag);
 log.info("freemarker自定義標簽配置完成!");
 }
}

有了這些配置后,就可以在頁面里使用了,具體用法如下

?
1
2
3
4
5
6
7
8
<#if sec.haspermission("topic:list")>
 <li <#if page_tab=='topic'>class="active"</#if>>
 <a href="/admin/topic/list" rel="external nofollow" >
  <i class="fa fa-list"></i>
  <span>話題列表</span>
 </a>
 </li>
</#if>

加上這個后,在渲染頁面的時候,就會根據當前用戶是否有查看話題列表的權限,然后來渲染這個菜單

注解權限

有了上面freemarker標簽判斷是否有權限來渲染頁面,這樣做只能防君子,不能防小人,如果一個人知道后臺的某個訪問鏈接,但這個鏈接它是沒有權限訪問的,那他只要手動輸入這個鏈接就還是可以訪問的,所以這里還要在controller層加一套防御,具體配置如下

在shiroconfig里加上兩個bean

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//加入注解的使用,不加入這個注解不生效
@bean
public authorizationattributesourceadvisor authorizationattributesourceadvisor(defaultwebsecuritymanager securitymanager) {
 authorizationattributesourceadvisor authorizationattributesourceadvisor = new authorizationattributesourceadvisor();
 authorizationattributesourceadvisor.setsecuritymanager(securitymanager);
 return authorizationattributesourceadvisor;
}
@bean
@conditionalonmissingbean
public defaultadvisorautoproxycreator defaultadvisorautoproxycreator() {
 defaultadvisorautoproxycreator defaultaap = new defaultadvisorautoproxycreator();
 defaultaap.setproxytargetclass(true);
 return defaultaap;
}

有了這兩個bean就可以用shiro的注解鑒權了,用法如下 @requirespermissions("topic:list")

 

?
1
2
3
4
5
6
7
8
9
10
11
@controller
@requestmapping("/admin/topic")
public class topicadmincontroller extends baseadmincontroller {
 
 @requirespermissions("topic:list")
 @getmapping("/list")
 public string list() {
 // todo
 return "admin/topic/list";
 }
}

shiro除了 @requirespermissions 注解外,還有其它幾個鑒權的注解

  • @requirespermissions
  • @requiresroles
  • @requiresuser
  • @requiresguest
  • @requiresauthentication

一般 @requirespermissions 就夠用了

總結

spring-boot 集成 shiro 到這就結束了,是不是網上能找到的教程里最全的!相比 spring-security 要簡單太多了,強烈推薦

原文鏈接:https://tomoya92.github.io/2018/12/05/spring-boot-shiro/

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产激情视频在线 | 亚洲网站免费 | 国产免费一区二区三区网站免费 | 91短视频在线播放 | 天天草夜夜 | 日日操夜夜透 | 国产在线精品一区二区不卡 | 亚洲一区二区三区高清视频 | 欧美高清在线精品一区二区不卡 | 一级网站 | 欧美成人午夜精品久久久 | 免费午夜视频 | 3级毛片 | 性欧美日本 | 成人羞羞在线观看网站 | 日本大片在线播放 | 欧美一级黄色录像片 | 欧美一级黄色录像片 | 国产免费一区二区三区网站免费 | 青青操精品 | 一本色道久久综合狠狠躁篇适合什么人看 | 欧美黑人xx | 成年免费大片黄在线观看岛国 | 午夜视频免费播放 | 调教小男生抽打尿孔嗯啊视频 | 一区二区三区日韩在线 | 国产羞羞视频在线观看免费应用 | 午夜视频亚洲 | av电影在线网| 久久男人视频 | 国产免费一区二区三区网站免费 | 欧美成人一区二区三区电影 | 国产亚洲精品综合一区91 | 久久久久久久久久久高潮一区二区 | 成人在线观看免费视频 | 欧美乱论 | 91网站在线观看视频 | 国产美女视频一区二区三区 | 免费国产一级淫片 | 毛片区| av在线网站观看 |