1、權限管理基礎(認證和授權):
前言
本文主要講解的知識點有以下:
- 權限管理的基礎知識
- 模型
- 粗粒度和細粒度的概念
- 回顧URL攔截的實現
- Shiro的介紹與簡單入門
一、Shiro基礎知識
在學習Shiro這個框架之前,首先我們要先了解Shiro需要的基礎知識:權限管理
1.1什么是權限管理?
只要有用戶參與的系統一般都要有權限管理,權限管理實現對用戶訪問系統的控制,按照安全規則或者安全策略控制用戶可以訪問而且只能訪問自己被授權的資源。
對權限的管理又分為兩大類別:
- 用戶認證
- 用戶授權
1.1.1用戶認證
用戶認證,用戶去訪問系統,系統要驗證用戶身份的合法性
最常用的用戶身份驗證的方法:1、用戶名密碼方式、2、指紋打卡機、3、基於證書驗證方法。。系統驗證用戶身份合法,用戶方可訪問系統的資源。
舉個例子:
- 當我們輸入了自己的淘寶的賬戶和密碼,才能打開購物車
用戶認證的流程:
- 判斷該資源能否不認證就能訪問【登陸頁面、首頁】
- 如果該資源需要認證后才能訪問,那么判斷該訪問者是否認證了
- 如果還沒有認證,那么需要返回到【登陸頁面】進行認證
- 認證通過后才能訪問資源

這里寫圖片描述
從用戶認證我們可以抽取出這么幾個概念
- subject主體:理解為用戶,可能是程序,都要去訪問系統的資源,系統需要對subject進行身份認證
- principal身份信息:通常是唯一的,一個主體還有多個身份信息,但是都有一個主身份信息(primary principal)【我們可以選擇身份證認證、學生證認證等等都是我們的身份信息】
- credential憑證信息:可以是密碼 、證書、指紋。
總結:主體在進行身份認證時需要提供身份信息和憑證信息。
1.1.2用戶授權
用戶授權,簡單理解為訪問控制,在用戶認證通過后,系統對用戶訪問資源進行控制,用戶具有資源的訪問權限方可訪問。
用戶授權的流程
- 到達了用戶授權環節,當然是需要用戶認證之后了
- 用戶訪問資源,系統判斷該用戶是否有權限去操作該資源
- 如果該用戶有權限才能夠訪問,如果沒有權限就不能訪問了

這里寫圖片描述
授權的過程可以簡單理解為:主體認證之后,系統進行訪問控制
subject必須具備資源的訪問權限才可訪問該資源..
權限/許可(permission) :針對資源的權限或許可,subject具有permission訪問資源,如何訪問/操作需要定義permission,權限比如:用戶添加、用戶修改、商品刪除
資源可以分為兩種
- 資源類型:系統的用戶信息就是資源類型,相當於java類。
- 資源實例:系統中id為001的用戶就是資源實例,相當於new的java對象。
1.2權限管理模型
一般地,我們可以抽取出這么幾個模型:
- 主體(賬號、密碼)
- 資源(資源名稱、訪問地址)
- 權限(權限名稱、資源id)
- 角色(角色名稱)
- 角色和權限關系(角色id、權限id)
- 主體和角色關系(主體id、角色id)

這里寫圖片描述
通常企業開發中將資源和權限表合並為一張權限表,如下:
- 資源(資源名稱、訪問地址)
- 權限(權限名稱、資源id)
合並為:
- 權限(權限名稱、資源名稱、資源訪問地址)

這里寫圖片描述
1.3分配權限
用戶需要分配相應的權限才可訪問相應的資源。權限是對於資源的操作許可。
通常給用戶分配資源權限需要將權限信息持久化,比如存儲在關系數據庫中。把用戶信息、權限管理、用戶分配的權限信息寫到數據庫(權限數據模型)
1.3.1基於角色訪問控制
RBAC(role based access control),基於角色的訪問控制。
角色針對人划分的,人作為用戶在系統中屬於活動內容,如果該 角色可以訪問的資源出現變更,需要修改你的代碼了,
基於角色的訪問控制是不利於系統維護(可擴展性不強)。
1.3.2基於資源的訪問控制
RBAC(Resource based access control),基於資源的訪問控制。
資源在系統中是不變的,比如資源有:類中的方法,頁面中的按鈕。
建議使用基於資源的訪問控制實現權限管理。
二、 粗粒度和細粒度權限
細粒度權限管理:對資源實例的權限管理。資源實例就資源類型的具體化,比如:用戶id為001的修改連接,1110班的用戶信息、行政部的員工。細粒度權限管理就是數據級別的權限管理。
粗粒度權限管理比如:超級管理員可以訪問戶添加頁面、用戶信息等全部頁面。部門管理員可以訪問用戶信息頁面包括 頁面中所有按鈕。
粗粒度和細粒度例子:
2.1如何實現粗粒度權限管理?
粗粒度權限管理比較容易將權限管理的代碼抽取出來在系統架構級別統一處理。比如:通過springmvc的攔截器實現授權。
對細粒度權限管理在數據級別是沒有共性可言,針對細粒度權限管理就是系統業務邏輯的一部分,在業務層去處理相對比較簡單
比如:部門經理只查詢本部門員工信息,在service接口提供一個部門id的參數,controller中根據當前用戶的信息得到該 用戶屬於哪個部門,調用service時將部門id傳入service,實現該用戶只查詢本部門的員工。
2.1.1基於URL攔截
基於url攔截的方式實現在實際開發中比較常用的一種方式。
對於web系統,通過filter過慮器實現url攔截,也可以springmvc的攔截器實現基於url的攔截。
2.2.2使用權限管理框架實現
對於粗粒度權限管理,建議使用優秀權限管理框架來實現,節省開發成功,提高開發效率。
shiro就是一個優秀權限管理框架。
三、回顧URL攔截
我們在學習的路途上也是使用過幾次URL對權限進行攔截的
當時我們做了權限的增刪該查的管理系統,但是在權限表中是沒有把資源添加進去,我們使用的是Map集合來進行替代的。 http://blog.csdn.net/hon_3y/article/details/61926175
隨后,我們學習了動態代理和注解,我們也做了一個基於注解的攔截
- 在Controller得到service對象的時候,service工廠返回的是一個動態代理對象回去
- Controller拿着代理對象去調用方法,代理對象就會去解析該方法上是否有注解
- 如果有注解,那么就需要我們進行判斷該主體是否認證了,如果認證了就判斷該主體是否有權限
- 當我們解析出該主體的權限和我們注解的權限是一致的時候,才放行!
http://blog.csdn.net/hon_3y/article/details/70767050
流程:

這里寫圖片描述
3.1認證的JavaBean
我們之前認證都是放在默認的Javabean對象上的,現在既然我們准備學Shiro了,我們就得專業一點,弄一個專門存儲認證信息的JavaBean
/** * 用戶身份信息,存入session 由於tomcat將session會序列化在本地硬盤上,所以使用Serializable接口 * * @author Thinkpad * */ public class ActiveUser implements java.io.Serializable { private String userid;//用戶id(主鍵) private String usercode;// 用戶賬號 private String username;// 用戶名稱 private List<SysPermission> menus;// 菜單 private List<SysPermission> permissions;// 權限 public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUsercode() { return usercode; } public void setUsercode(String usercode) { this.usercode = usercode; } public String getUserid() { return userid; } public void setUserid(String userid) { this.userid = userid; } public List<SysPermission> getMenus() { return menus; } public void setMenus(List<SysPermission> menus) { this.menus = menus; } public List<SysPermission> getPermissions() { return permissions; } public void setPermissions(List<SysPermission> permissions) { this.permissions = permissions; } }
認證的服務
@Override public ActiveUser authenticat(String userCode, String password) throws Exception { /** 認證過程: 根據用戶身份(賬號)查詢數據庫,如果查詢不到用戶不存在 對輸入的密碼 和數據庫密碼 進行比對,如果一致,認證通過 */ //根據用戶賬號查詢數據庫 SysUser sysUser = this.findSysUserByUserCode(userCode); if(sysUser == null){ //拋出異常 throw new CustomException("用戶賬號不存在"); } //數據庫密碼 (md5密碼 ) String password_db = sysUser.getPassword(); //對輸入的密碼 和數據庫密碼 進行比對,如果一致,認證通過 //對頁面輸入的密碼 進行md5加密 String password_input_md5 = new MD5().getMD5ofStr(password); if(!password_input_md5.equalsIgnoreCase(password_db)){ //拋出異常 throw new CustomException("用戶名或密碼 錯誤"); } //得到用戶id String userid = sysUser.getId(); //根據用戶id查詢菜單 List<SysPermission> menus =this.findMenuListByUserId(userid); //根據用戶id查詢權限url List<SysPermission> permissions = this.findPermissionListByUserId(userid); //認證通過,返回用戶身份信息 ActiveUser activeUser = new ActiveUser(); activeUser.setUserid(sysUser.getId()); activeUser.setUsercode(userCode); activeUser.setUsername(sysUser.getUsername());//用戶名稱 //放入權限范圍的菜單和url activeUser.setMenus(menus); activeUser.setPermissions(permissions); return activeUser; }
Controller處理認證,如果身份認證成功,那么把認證信息存儲在Session中
@RequestMapping("/login") public String login(HttpSession session, String randomcode,String usercode,String password)throws Exception{ //校驗驗證碼,防止惡性攻擊 //從session獲取正確驗證碼 String validateCode = (String) session.getAttribute("validateCode"); //輸入的驗證和session中的驗證進行對比 if(!randomcode.equals(validateCode)){ //拋出異常 throw new CustomException("驗證碼輸入錯誤"); } //調用service校驗用戶賬號和密碼的正確性 ActiveUser activeUser = sysService.authenticat(usercode, password); //如果service校驗通過,將用戶身份記錄到session session.setAttribute("activeUser", activeUser); //重定向到商品查詢頁面 return "redirect:/first.action"; }
身份認證攔截器
//在執行handler之前來執行的 //用於用戶認證校驗、用戶權限校驗 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //得到請求的url String url = request.getRequestURI(); //判斷是否是公開 地址 //實際開發中需要公開 地址配置在配置文件中 //從配置中取逆名訪問url List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL"); //遍歷公開 地址,如果是公開 地址則放行 for(String open_url:open_urls){ if(url.indexOf(open_url)>=0){ //如果是公開 地址則放行 return true; } } //判斷用戶身份在session中是否存在 HttpSession session = request.getSession(); ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser"); //如果用戶身份在session中存在放行 if(activeUser!=null){ return true; } //執行到這里攔截,跳轉到登陸頁面,用戶進行身份認證 request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); //如果返回false表示攔截不繼續執行handler,如果返回true表示放行 return false; }
授權攔截器
//在執行handler之前來執行的 //用於用戶認證校驗、用戶權限校驗 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //得到請求的url String url = request.getRequestURI(); //判斷是否是公開 地址 //實際開發中需要公開 地址配置在配置文件中 //從配置中取逆名訪問url List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL"); //遍歷公開 地址,如果是公開 地址則放行 for(String open_url:open_urls){ if(url.indexOf(open_url)>=0){ //如果是公開 地址則放行 return true; } } //從配置文件中獲取公共訪問地址 List<String> common_urls = ResourcesUtil.gekeyList("commonURL"); //遍歷公用 地址,如果是公用 地址則放行 for(String common_url:common_urls){ if(url.indexOf(common_url)>=0){ //如果是公開 地址則放行 return true; } } //獲取session HttpSession session = request.getSession(); ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser"); //從session中取權限范圍的url List<SysPermission> permissions = activeUser.getPermissions(); for(SysPermission sysPermission:permissions){ //權限的url String permission_url = sysPermission.getUrl(); if(url.indexOf(permission_url)>=0){ //如果是權限的url 地址則放行 return true; } } //執行到這里攔截,跳轉到無權訪問的提示頁面 request.getRequestDispatcher("/WEB-INF/jsp/refuse.jsp").forward(request, response); //如果返回false表示攔截不繼續執行handler,如果返回true表示放行 return false; }
攔截器配置:
<!--攔截器 --> <mvc:interceptors> <mvc:interceptor> <!-- 用戶認證攔截 --> <mvc:mapping path="/**" /> <bean class="cn.itcast.ssm.controller.interceptor.LoginInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <!-- 授權攔截 --> <mvc:mapping path="/**" /> <bean class="cn.itcast.ssm.controller.interceptor.PermissionInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
理解了認證和授權之后,有關Shiro更多內容轉自
Shiro入門(認證授權):https://cloud.tencent.com/developer/article/1080944
2、這里是一個Shiro Demo:
Shiro Demo:https://www.sojson.com/shiro
源代碼:https://github.com/baichengzhou/SpringMVC-Mybatis-Shiro-redis-0.2
技術選型:SpringMVC+Spring+Shiro+Freemarker+MyBatis+Redis+Mysql
Freemarker使用Shiro:https://www.sojson.com/blog/143.html
JSP使用Shiro :https://www.sojson.com/blog/144.html
RBAC3(權限的增刪改查):https://www.sojson.com/blog/141.html
3、相關文章:
單點登錄(SSO):https://yq.aliyun.com/articles/636281
淺談對JS跨域問題的理解及sessionid共享問題:https://blog.csdn.net/yjx891215/article/details/82350554
理解Cookie和Session的區別及使用:https://blog.csdn.net/liyifan687/article/details/80077928
Shiro入門(認證授權):https://cloud.tencent.com/developer/article/1080944
JeeSite 4.0 功能權限管理基礎(Shiro):https://my.oschina.net/thinkgem/blog/1834877
4、Shiro運行流程圖: