Apache Shiro 快速入門教程
第一部分 什麼是Apache Shiro
1、什麼是 apache shiro :
Apache Shiro是一個功能強大且易於使用的Java安全框架,提供了認證,授權,加密,和會話管理
如同 spring security 一樣都是是一個許可權安全框架,但是與Spring Security相比,在於他使用了和比較簡潔易懂的認證和授權方式。
2、Apache Shiro 的三大核心元件:
1、Subject :當前使用者的操作
2、SecurityManager:用於管理所有的Subject
3、Realms:用於進行許可權資訊的驗證
Subject:即當前使用者,在許可權管理的應用程式裡往往需要知道誰能夠操作什麼,誰擁有操作該程式的權利,shiro中則需要通過Subject來提供基礎的當前使用者資訊,Subject 不僅僅代表某個使用者,也可以是第三方程序、後臺帳戶(Daemon Account)或其他類似事物。
SecurityManager:即所有Subject的管理者,這是Shiro框架的核心元件,可以把他看做是一個Shiro框架的全域性管理元件,用於排程各種Shiro框架的服務。
Realms:Realms則是使用者的資訊認證器和使用者的許可權人證器,我們需要自己來實現Realms來自定義的管理我們自己系統內部的許可權規則。
3、Authentication 和 Authorization
在shiro的使用者許可權認證過程中其通過兩個方法來實現:
1、Authentication:是驗證使用者身份的過程。
2、Authorization:是授權訪問控制,用於對使用者進行的操作進行人證授權,證明該使用者是否允許進行當前操作,如訪問某個連結,某個資原始檔等。
4、其他元件:
除了以上幾個元件外,Shiro還有幾個其他元件:
1、SessionManager :Shiro為任何應用提供了一個會話程式設計正規化。
2、CacheManager :對Shiro的其他元件提供快取支援。
5、Shiro 完整架構圖:
圖片轉自:http://kdboy.iteye.com/blog/1154644
第二部分 Apache Shiro 整合Spring的Web程式構建
1、準備工具:
持久層框架:Hibernate4 這邊我使用了hibernate來對資料持久層進行操作
控制顯示層框架:SpringMVC 這邊我使用了SpringMVC實際開發中也可以是其他框架
準備好所需要的jar放到專案中。
2、建立資料庫:
首先需要四張表,分別為 user(使用者)、role(角色)、permission(許可權)、userRole(使用者角色關係表)
這邊分別建立四張表的實體類,通過Hiberante的hibernate.hbm2ddl.auto屬性的update 來自動生成資料表結構。
[java] view plain copy print ?
- /***
- * 使用者表
- *
- * @author Swinglife
- *
- */
- @Table(name = "t_user")
- @Entity
- public class User {
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- Integer id;
- /** 使用者名稱 **/
- String username;
- /** 密碼 **/
- String password;
- /** 是否刪除 **/
- Integer isDelete;
- /** 建立時間 **/
- Date createDate;
- //多對多使用者許可權表
- @OneToMany(mappedBy = "user",cascade=CascadeType.ALL)
- List<UserRole> userRoles;
- 省略get set….
- }
[java] view plain copy print ?
- /****
- * 角色表
- *
- * @author Swinglife
- *
- */
- @Entity
- @Table(name = "t_role")
- public class Role {
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- Integer id;
- /**角色名**/
- String name;
- /**角色說明**/
- String description;
- }
[java] view plain copy print ?
- /****
- * 許可權表
- *
- * @author Swinglife
- *
- */
- @Entity
- @Table(name = "t_permission")
- public class Permission {
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- Integer id;
- /**token**/
- String token;
- /**資源url**/
- String url;
- /**許可權說明**/
- String description;
- /**所屬角色編號**/
- Integer roleId;
- }
[java] view plain copy print ?
- /***
- * 使用者角色表
- *
- * @author Swinglife
- *
- */
- @Entity
- @Table(name = "t_user_role")
- public class UserRole {
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- Integer id;
- @ManyToOne(cascade = CascadeType.ALL)
- @JoinColumn(name = "userId", unique = true)
- User user;
- @ManyToOne
- @JoinColumn(name = "roleId", unique = true)
- Role role;
- }
3、編寫操作使用者業務的Service:
[java] view plain copy print ?
- @Service
- public class AccountService {
- /****
- * 通過使用者名稱獲取使用者物件
- *
- * @param username
- * @return
- */
- public User getUserByUserName(String username) {
- User user = (User) dao.findObjectByHQL("FROM User WHERE username = ?", new Object[] { username });
- return user;
- }
- /***
- * 通過使用者名稱獲取許可權資源
- *
- * @param username
- * @return
- */
- public List<String> getPermissionsByUserName(String username) {
- System.out.println("呼叫");
- User user = getUserByUserName(username);
- if (user == null) {
- return null;
- }
- List<String> list = new ArrayList<String>();
- // System.out.println(user.getUserRoles().get(0).get);
- for (UserRole userRole : user.getUserRoles()) {
- Role role = userRole.getRole();
- List<Permission> permissions = dao.findAllByHQL("FROM Permission WHERE roleId = ?", new Object[] { role.getId() });
- for (Permission p : permissions) {
- list.add(p.getUrl());
- }
- }
- return list;
- }
- // 公共的資料庫訪問介面
- // 這裡省略BaseDao dao的編寫
- @Autowired
- private BaseDao dao;
- }
4、編寫shiro元件自定義Realm:
[java] view plain copy print ?
- package org.swinglife.shiro;
- import java.util.List;
- import org.apache.shiro.authc.AuthenticationException;
- import org.apache.shiro.authc.AuthenticationInfo;
- import org.apache.shiro.authc.AuthenticationToken;
- import org.apache.shiro.authc.SimpleAuthenticationInfo;
- import org.apache.shiro.authc.UsernamePasswordToken;
- import org.apache.shiro.authz.AuthorizationInfo;
- import org.apache.shiro.authz.SimpleAuthorizationInfo;
- import org.apache.shiro.realm.AuthorizingRealm;
- import org.apache.shiro.subject.PrincipalCollection;
- import org.swinglife.model.User;
- import org.swinglife.service.AccountService;
- /****
- * 自定義Realm
- *
- * @author Swinglife
- *
- */
- public class MyShiroRealm extends AuthorizingRealm {
- /***
- * 獲取授權資訊
- */
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
- //根據自己系統規則的需要編寫獲取授權資訊,這裡為了快速入門只獲取了使用者對應角色的資源url資訊
- String username = (String) pc.fromRealm(getName()).iterator().next();
- if (username != null) {
- List<String> pers = accountService.getPermissionsByUserName(username);
- if (pers != null && !pers.isEmpty()) {
- SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
- for (String each : pers) {
- //將許可權資源新增到使用者資訊中
- info.addStringPermission(each);
- }
- return info;
- }
- }
- return null;
- }
- /***
- * 獲取認證資訊
- */
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken at) throws AuthenticationException {
- UsernamePasswordToken token = (UsernamePasswordToken) at;
- // 通過表單接收的使用者名稱
- String username = token.getUsername();
- if (username != null && !"".equals(username)) {
- User user = accountService.getUserByUserName(username);
- if (user != null) {
- return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
- }
- }
- return null;
- }
- /**使用者的業務類**/
- private AccountService accountService;
- public AccountService getAccountService() {
- return accountService;
- }
- public void setAccountService(AccountService accountService) {
- this.accountService = accountService;
- }
- }
上述類繼承了Shiro的AuthorizingRealm類 實現了AuthorizationInfo和AuthenticationInfo兩個方法,用於獲取使用者許可權和認證使用者登入資訊
5、編寫LoginController:
[java] view plain copy print ?
- package org.swinglife.controller;
- import org.apache.shiro.SecurityUtils;
- import org.apache.shiro.authc.UsernamePasswordToken;
- import org.apache.shiro.subject.Subject;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
- import org.springframework.web.portlet.ModelAndView;
- import org.swinglife.model.User;
- import org.swinglife.service.AccountService;
- /****
- * 使用者登入Controller
- *
- * @author Swinglife
- *
- */
- @Controller
- public class LoginController {
- /***
- * 跳轉到登入頁面
- *
- * @return
- */
- @RequestMapping(value = "toLogin")
- public String toLogin() {
- // 跳轉到/page/login.jsp頁面
- return "login";
- }
- /***
- * 實現使用者登入
- *
- * @param username
- * @param password
- * @return
- */
- @RequestMapping(value = "login")
- public ModelAndView Login(String username, String password) {
- ModelAndView mav = new ModelAndView();
- User user = accountService.getUserByUserName(username);
- if (user == null) {
- mav.setView("toLogin");
- mav.addObject("msg", "使用者不存在");
- return mav;
- }
- if (!user.getPassword().equals(password)) {
- mav.setView("toLogin");
- mav.addObject("msg", "賬號密碼錯誤");
- return mav;
- }
- SecurityUtils.getSecurityManager().logout(SecurityUtils.getSubject());
- // 登入後存放進shiro token
- UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
- Subject subject = SecurityUtils.getSubject();
- subject.login(token);
- // 登入成功後會跳轉到successUrl配置的連結,不用管下面返回的連結。
- mav.setView("redirect:/home");
- return mav;
- }
- // 處理使用者業務類
- @Autowired
- private AccountService accountService;
- }
6、編寫資訊認證成功後的跳轉頁面:
[java] view plain copy print ?
- package org.swinglife.controller;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- @Controller
- public class IndexController {
- @RequestMapping("home")
- public String index() {
- System.out.println("登入成功");
- return "home";
- }
- }
7、Shiro的配置檔案.xml
[java] view plain copy print ?
- <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
- <property name="securityManager" ref="securityManager" />
- <property name="loginUrl" value="/toLogin" />
- <property name="successUrl" value="/home" />
- <property name="unauthorizedUrl" value="/403" />
- <property name="filterChainDefinitions">
- <value>
- /toLogin = authc <!-- authc 表示需要認證才能訪問的頁面 -->
- /home = authc, perms[/home] <!-- perms 表示需要該許可權才能訪問的頁面 -->
- </value>
- </property>
- </bean>
- <bean id="myShiroRealm" class="org.swinglife.shiro.MyShiroRealm">
- <property name="accountService" ref="accountService" />
- </bean>
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
- <property name="realm" ref="myShiroRealm"></property>
- </bean>
- <bean id="accountService" class="org.swinglife.service.AccountService"></bean>
- <!-- <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
- <property name="cacheManager" ref="cacheManager" /> </bean> -->
loginUrl 用於配置登陸頁
successUrl 用於配置登入成功後返回的頁面,不過該引數只會在當登入頁面中並沒有任何返回頁面時才會生效,否則會跳轉到登入Controller中的指定頁面。
unauthorizedUrl 用於配置沒有許可權訪問頁面時跳轉的頁面
filterChainDefinitions:apache shiro通過filterChainDefinitions引數來分配連結的過濾,資源過濾有常用的以下幾個引數:
1、authc 表示需要認證的連結
2、perms[/url] 表示該連結需要擁有對應的資源/許可權才能訪問
3、roles[admin] 表示需要對應的角色才能訪問
4、perms[admin:url] 表示需要對應角色的資源才能訪問
8、登陸頁login.jsp
[html] view plain copy print ?- <body>
- <h1>user login</h1>
- <form action="login" method="post">
- username:<input type="text" name="username"><p>
- password:<input type="password" name="password">
- <p>
- ${msg }
- <input type="submit" value="submit">
- </form>
- </body>
9、執行程式
在資料庫中新增一條使用者、角色、以及許可權資料,並且在關聯表中新增一條關聯資料:
在瀏覽器中訪問: home頁面 就會跳轉到登入頁面:
轉載出處:
最後輸入 賬號密碼 就會跳轉
shiro jar:http://download.csdn.net/detail/swingpyzf/8766673
專案原始碼:github:https://github.com/swinglife/shiro_ex