Shiro之详细入门附Demo

来源:互联网 发布:imf的数据 编辑:程序博客网 时间:2024/05/14 17:54

第一步:Apache Shiro简介:
Apache Shiro 是一个强大而灵活的开源安全框架,它干净利落地处理身份认证,授权,企业会话管理和加密。
Apache Shiro 的首要目标是易于使用和理解。安全有时候是很复杂的,甚至是痛苦的,但它没有必要这样。框架应
该尽可能掩盖复杂的地方,露出一个干净而直观的 API,来简化开发人员在使他们的应用程序安全上的努力。
以下是你可以用 Apache Shiro 所做的事情:

  • 验证用户来核实他们的身份
  • 对用户执行访问控制,如:
    • 判断用户是否被分配了一个确定的安全角色
    • 判断用户是否被允许做某事
  • 在任何环境下使用 Session API,即使没有 Web 或 EJB 容器。
  • 在身份验证,访问控制期间或在会话的生命周期,对事件作出反应。
  • 聚集一个或多个用户安全数据的数据源,并作为一个单一的复合用户“视图”。
  • 启用单点登录(SSO)功能。
  • 为没有关联到登录的用户启用”Remember Me”服务

    Shiro 视图在所有应用程序环境下实现这些目标——从最简单的命令行应用程序到最大的企业应用,不强制依赖其
    他第三方框架,容器,或应用服务器。当然,该项目的目标是尽可能地融入到这些环境,但它能够在任何环境下立
    即可用

这里写图片描述

Shiro 把 Shiro 开发团队称为“应用程序的四大基石” ——身份验证,授权,会话管理和加密作为其目标。

  • Authentication:有时也简称为“登录”,这是一个证明用户是他们所说的他们是谁的行为。
  • Authorization:访问控制的过程,也就是绝对“谁”去访问“什么”。
  • Session Management:管理用户特定的会话,即使在非 Web 或 EJB 应用程序。
  • Cryptography:通过使用加密算法保持数据安全同时易于使用。

也提供了额外的功能来支持和加强在不同环境下所关注的方面,尤其是以下这些:

  • Web Support: Shiro 的 web 支持的 API 能够轻松地帮助保护 Web 应用程序。
  • Caching:缓存是 Apache Shiro 中的第一层公民,来确保安全操作快速而又高效。
  • Concurrency: Apache Shiro 利用它的并发特性来支持多线程应用程序。
  • Testing:测试支持的存在来帮助你编写单元测试和集成测试,并确保你的能够如预期的一样安全。
  • “Run As”:一个允许用户假设为另一个用户身份(如果允许)的功能,有时候在管理脚本很有用。
  • “Remember Me”:在会话中记住用户的身份,所以他们只需要在强制时候登录。

第二步:搭建平台
官方网站给的官方示例是基于INI的,将用户角色信息和权限信息和各种组件都统一配置在shiro.ini配置文件中。这种方式虽然是官方推荐的,但是作为java开发人员,暂时是不能接受的。我就要XML!0.0
研究了网上很多demo实例,或简单或复杂。比较难以理解,在看过很多示例和API之后整合了一个最简最容易理解的一个小demo。
首先熟悉一下基本术语:
Subject(org.apache.shiro.subject.Subject):Subject 是一个安全术语,它基本上的意思是“当前正在执行的用户的特定的安全视图”。你可以把 Subject 看成是 Shiro 的”User”概念。
SecurityManager(org.apache.shiro.session.SessionManager):SecurityManager 是 Shiro 架构的心脏,并作为一种“保护伞”对象来协调内部的安全组件共同构成一个对象图。然而,一旦 SecurityManager 和它的内置对象图已经配置给一个应用程序,那么它单独留下来,且应用程序开发人员几乎使用他们所有的时间来处理 Subject API。
所有 Subject 实例都被绑定到(且这是必须的)一个 SecurityManager 上。当你与一个 Subject 交互时,那些交互作用转化为与 SecurityManager 交互的特定 subject 的交互作用
这里写图片描述
Authenticator(org.apache.shiro.authc.Authenticator):Authenticator 是一个对执行及对用户的身份验证(登录)尝试负责的组件。当一个用户尝试登录时,该逻辑被 Authenticator 执行。 Authenticator 知道如何与一个或多个 Realm 协调来存储相关的用户/帐户信息。从这些Realm 中获得的数据被用来验证用户的身份来保证用户确实是他们所说的他们是谁。
Authorizer(org.apache.shiro.authz.Authorizer):Authorizer 是负责在应用程序中决定用户的访问控制的组件。它是一种最终判定用户是否被允许做某事的机制。
与 Authenticator 相似, Authorizer 也知道如何协调多个后台数据源来访问角色恶化权限信息。 Authorizer 使用该信息来准确地决定用户是否被允许执行给定的动作。
SessionManager(org.apache.shiro.session.SessionManager):SessionManager 知道如何去创建及管理用户 Session 生命周期来为所有环境下的用户提供一个强健的 Session体验。这在安全框架界是一个独有的特色——Shiro 拥有能够在任何环境下本地化管理用户 Session 的能力,即使没有可用的 Web/Servlet 或 EJB 容器,它将会使用它内置的企业级会话管理来提供同样的编程体验。SessionDAO 的存在允许任何数据源能够在持久会话中使用。SessionDAO(org.apache.shiro.session.mgt.eis.SessionDAO)代表 SessionManager 执行 Session 持久化(CRUD)操作。这允许任何数据存储被插入到会话管理的基础之中
CacheManager(org.apahce.shiro.cache.CacheManager):CacheManager 创建并管理其他 Shiro 组件使用的 Cache 实例生命周期。因为 Shiro 能够访问许多后台数据源,由于身份验证,授权和会话管理,缓存在框架中一直是一流的架构功能,用来在同时使用这些数据源时提高性能。任何现代开源和/或企业的缓存产品能够被插入到 Shiro 来提供一个快速及高效的用户体验
Cryptography(org.apache.shiro.crypto.*):Cryptography 是对企业安全框架的一个很自然的补充。加密
Realms(org.apache.shiro.realm.Realm):Realms 在 Shiro 和你的应用程序的安全数据之间担当“桥梁”或“连接器”。当它实际上与安全相关的数据如用来执行身份验证(登录)及授权(访问控制)的用户帐户交互时, Shiro 从一个或多个为应用程序配置的 Realm 中寻找许多这样的东西。你可以按你的需要配置多个 Realm(通常一个数据源一个 Realm),且 Shiro 将为身份验证和授权对它们进行必要的协调
有点扯远了0.0
第三步:整合SSM和Shiro

  1. 搭建基本的SSM框架
    引入shiro依赖肯定是必须的,区别的是在web.xml中添加Shiro的过滤器。
<!-- 配置Shiro的过虑器,如果与Spring集成,则必须要使用Shiro提供的过虑器代理 -->    <filter>        <filter-name>shiroFilter</filter-name>        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>        <init-param>            <!-- 指定SpringBean代理Bean的名称,如果没有指定则与过虑器名称保持一致 -->            <param-name>targetBeanName</param-name>            <param-value>shiroFilter</param-value>        </init-param>        <init-param>            <!-- 配置是否启动过虑器的init/destory方法 -->            <param-name>targetFilterLifecycle</param-name>            <param-value>true</param-value>        </init-param>    </filter>    <filter-mapping>        <filter-name>shiroFilter</filter-name>        <url-pattern>/*</url-pattern>        <dispatcher>REQUEST</dispatcher>        <dispatcher>FORWARD</dispatcher>        <dispatcher>INCLUDE</dispatcher>        <dispatcher>ERROR</dispatcher>    </filter-mapping>    <!-- 配置如果没有权限,则跳转到的页面 -->    <error-page>        <exception-type>org.apache.shiro.authz.AuthorizationException</exception-type>        <location>/denied.jsp</location>    </error-page>    <error-page>        <exception-type>org.apache.shiro.authz.UnauthorizedException</exception-type>        <location>/denied2.jsp</location>    </error-page>

spring_mvc.xml:(配置在DispatcherServlet中)

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:task="http://www.springframework.org/schema/task" xmlns:mvc="http://www.springframework.org/schema/mvc"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"    xmlns:context="http://www.springframework.org/schema/context"    xmlns:aop="http://www.springframework.org/schema/aop"    xsi:schemaLocation="http://www.springframework.org/schema/beans     http://www.springframework.org/schema/beans/spring-beans.xsd     http://www.springframework.org/schema/context     http://www.springframework.org/schema/context/spring-context-4.0.xsd     http://www.springframework.org/schema/task    http://www.springframework.org/schema/task/spring-task-4.0.xsd     http://www.springframework.org/schema/aop   http://www.springframework.org/schema/aop/spring-aop-4.0.xsd    http://www.springframework.org/schema/mvc     http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">    <!-- 自动扫描(自动注入) -->    <context:component-scan base-package="cn.shiro" />    <mvc:annotation-driven />    <bean name="springContextUtil" class="cn.shiro.utils.SpringContextUtil"        scope="singleton"></bean>    <aop:config proxy-target-class="true" />    <bean id="viewResolver"        class="org.springframework.web.servlet.view.InternalResourceViewResolver"        p:prefix="/WEB-INF/views/" p:suffix=".jsp" /></beans>

注:<aop:config proxy-target-class="true" />没有的话可能会出现身份验证的时候验证角色不验证权限。
spring_mybaits.xml:(通过mybatis连接数据库,不需要改动,直接拉过来)
spring_shiro.xml:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:context="http://www.springframework.org/schema/context"    xmlns:mvc="http://www.springframework.org/schema/mvc"    xsi:schemaLocation="    http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd    http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context-4.0.xsd    http://www.springframework.org/schema/mvc    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">    <!-- 授权 认证 -->    <bean id="sampleRealm" class="cn.shiro.realm.SampleRealm"></bean>       <!-- 配置SecurityManager并指定Realm -->    <!-- 安全管理器 -->    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">        <property name="realm" ref="sampleRealm" />    </bean>    <!-- 配置与shiro的整合,此名称与web.xml中配置的targetBeanName相同 -->    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">        <!-- 引用SecurityManager -->        <property name="securityManager" ref="securityManager" />        <!-- 登录的url -->        <property name="loginUrl" value="/login.jsp" />        <!-- 登录成功的url -->        <property name="successUrl" value="/index.jsp" />        <!-- 如果访问不成功则显示的url为 -->        <property name="unauthorizedUrl" value="/denied.jsp" />        <!-- 定义安全的页面及访问规则 -->        <property name="filterChainDefinitions">            <value>                /one/**=authc                /login.jsp=authc                /logout=logout                /**=authc            </value>        </property>    </bean>    <!-- 静态注入,相当于调用SecurityUtils.setSecurityManager(securityManager) -->    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">        <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>        <property name="arguments" ref="securityManager"/>    </bean>    <!-- 必须配置这个类 -->    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />    <!-- 以下是两个启用注解的权限控制 Enable Shiro Annotations for Spring-configured beans. the lifecycleBeanProcessor has run: -->    <bean       class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"        depends-on="lifecycleBeanPostProcessor" />    <bean        class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">        <property name="securityManager" ref="securityManager" />    </bean></beans>

SampleRealm.java:

package cn.shiro.realm;import java.util.Date;import java.util.HashSet;import java.util.Set;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.DisabledAccountException;import org.apache.shiro.authc.SimpleAuthenticationInfo;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.apache.shiro.subject.SimplePrincipalCollection;import cn.shiro.pojo.UUser;public class SampleRealm extends AuthorizingRealm {    /**     *  认证信息,主要针对用户登录,      */    @Override    protected AuthenticationInfo doGetAuthenticationInfo(            AuthenticationToken authcToken) throws AuthenticationException {        String username = (String) authcToken.getPrincipal();        String password = new String((char[]) authcToken.getCredentials());        UUser user = new UUser();        user.setEmail(username);        user.setId(1L);        user.setPswd(password);        user.setStatus(1L);        System.err.println(user);        if(UUser._0.equals(user.getStatus())){            throw new DisabledAccountException("帐号已经禁止登录!");        }else{            //更新登录时间 last login time            user.setLastLoginTime(new Date());        }        return new SimpleAuthenticationInfo(user,user.getPswd(), getName());    }     /**      * 授权      */      @Override    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {          SimpleAuthorizationInfo info =  new SimpleAuthorizationInfo();        UUser user = (UUser)principals.getPrimaryPrincipal();        //根据用户ID查询角色(role),放入到Authorization里。        Set<String> roles = new HashSet<String>();        roles.add(user.getEmail());        info.setRoles(roles);        System.out.println(roles);        //根据用户ID查询权限(permission),放入到Authorization里。        Set<String> permissions = new HashSet<String>();        permissions.add("admin:insert");        permissions.add("admin:del");        System.out.println(permissions.toString());        info.setStringPermissions(permissions);        return info;      }      /**     * 清空当前用户权限信息     */    public  void clearCachedAuthorizationInfo() {        PrincipalCollection principalCollection = SecurityUtils.getSubject().getPrincipals();        SimplePrincipalCollection principals = new SimplePrincipalCollection(                principalCollection, getName());        super.clearCachedAuthorizationInfo(principals);    }    /**     * 指定principalCollection 清除     */    public void clearCachedAuthorizationInfo(PrincipalCollection principalCollection) {        SimplePrincipalCollection principals = new SimplePrincipalCollection(                principalCollection, getName());        super.clearCachedAuthorizationInfo(principals);    }}

注:AuthorizingRealm是必须继承的。实现里面的doGetAuthenticationInfo(验证角色)doGetAuthorizationInfo(查询权限)方法。没有连接数据库,查询数据库验证权限这一步我直接使用死数据来模拟。之前研究角色表关系走了很大的弯路,透过现象看本质,本质上就是查询出角色写入到对象中去。
controller.java

package cn.shiro.controller;import org.apache.shiro.authz.annotation.RequiresAuthentication;import org.apache.shiro.authz.annotation.RequiresPermissions;import org.apache.shiro.authz.annotation.RequiresRoles;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;@Controller@RequestMapping("/one/")public class OneController {    @RequiresAuthentication // 要求用户必须要登录才可以访问这个资源    @RequiresRoles("admin") // 要求用户必须是admin角色才可以访问这个方法    @RequiresPermissions("admin:del") // 要求的权限    @RequestMapping("show")    @ResponseBody    public String show() {        System.err.println("Show.." + this);        return "b";    }}

UUser.java

package cn.shiro.pojo;import java.io.Serializable;import java.util.Date;import net.sf.json.JSONObject;public class UUser implements Serializable {    private static final long serialVersionUID = 1L;    // 0:禁止登录    public static final Long _0 = new Long(0);    // 1:有效    public static final Long _1 = new Long(1);    private Long id;    /** 昵称 */    private String nickname;    /** 邮箱 | 登录帐号 */    private String email;    /** 密码 */    private transient String pswd;    /** 创建时间 */    private Date createTime;    /** 最后登录时间 */    private Date lastLoginTime;    /** 1:有效,0:禁止登录 */    private Long status;    public UUser() {    }    public UUser(UUser user) {        this.id = user.getId();        this.nickname = user.getNickname();        this.email = user.getEmail();        this.pswd = user.getPswd();        this.createTime = user.getCreateTime();        this.lastLoginTime = user.getLastLoginTime();    }    public Long getId() {        return id;    }    public void setId(Long id) {        this.id = id;    }    public String getNickname() {        return nickname;    }    public void setNickname(String nickname) {        this.nickname = nickname;    }    public String getEmail() {        return email;    }    public Long getStatus() {        return status;    }    public void setStatus(Long status) {        this.status = status;    }    public void setEmail(String email) {        this.email = email;    }    public String getPswd() {        return pswd;    }    public void setPswd(String pswd) {        this.pswd = pswd;    }    public Date getCreateTime() {        return createTime;    }    public void setCreateTime(Date createTime) {        this.createTime = createTime;    }    public Date getLastLoginTime() {        return lastLoginTime;    }    public void setLastLoginTime(Date lastLoginTime) {        this.lastLoginTime = lastLoginTime;    }    public String toString() {        return JSONObject.fromObject(this).toString();    }}

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%><%@ taglib uri="http://shiro.apache.org/tags" prefix="s"%><!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title></head><body>    <s:authenticated>登录成功:   <s:principal></s:principal>        <br>        <a href="${pageContext.request.contextPath}/logout">退出</a>    </s:authenticated>    <s:notAuthenticated>        <p>登录</p>        <form action="" method="post">            Name:<input type="text" name="username"><br> Pwd:<input                type="text" name="password"><br> <input type="submit">        </form>    </s:notAuthenticated></body></html>

这是最简单的shiro配置,刨去了sessionManager、CacheManager、加密等配置,后面再慢慢加。
OK

原创粉丝点击