黑马程序员技术交流社区

标题: Apache Shiro 快速入门教程,shiro 基础教程 [打印本页]

作者: 曹老师    时间: 2017-8-21 22:30
标题: Apache Shiro 快速入门教程,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实际开发中也可以是其他框架
数据库MySQL
准备好所需要的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页面 就会跳转到登录页面:  



最后输入 账号密码 就会跳转到登录成功页面。






欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2