• Shiro入门(1)


    =============基本概念===================

    什么是Apache Shiro?

    Apache Shiro(发音为“shee-roh”,日语“堡垒(Castle)”的意思)是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。

    Shiro为解决下列问题(我喜欢称它们为应用安全的四要素)提供了保护应用的API:

    • 认证 - 用户身份识别,常被称为用户“登录”;

    • 授权 - 访问控制;

    • 密码加密 - 保护或隐藏数据防止被偷窥;

    • 会话管理 - 每用户相关的时间敏感的状态。

    Shiro还支持一些辅助特性,如Web应用安全、单元测试和多线程,它们的存在强化了上面提到的四个要素。

    核心概念:Subject,SecurityManager和Realms

    Subject

    在考虑应用安全时,你最常问的问题可能是“当前用户是谁?”或“当前用户允许做X吗?”。当我们写代码或设计用户界面时,问自己这些问题很平常:应用通常都是基于用户故事构建的,并且你希望功能描述(和安全)是基于每个用户的。所以,对于我们而言,考虑应用安全的最自然方式就是基于当前用户。Shiro的API用它的Subject概念从根本上体现了这种思考方式。

    Subject一词是一个安全术语,其基本意思是“当前的操作用户”。称之为“用户”并不准确,因为“用户”一词通常跟人相关。在安全领域,术语“Subject”可以是人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。

    SecurityManager

    Subject的“幕后”推手是SecurityManager。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。它是Shiro框架的核心,充当“保护伞”,引用了多个内部嵌套安全组件,它们形成了对象图。但是,一旦SecurityManager及其内部对象图配置好,它就会退居幕后,应用开发人员几乎把他们的所有时间都花在Subject API调用上。

    那么,如何设置SecurityManager呢?嗯,这要看应用的环境。例如,Web应用通常会在Web.xml中指定一个Shiro Servlet Filter,这会创建SecurityManager实例,如果你运行的是一个独立应用,你需要用其他配置方式,但有很多配置选项。

    一个应用几乎总是只有一个SecurityManager实例。它实际是应用的Singleton(尽管不必是一个静态Singleton)。跟Shiro里的几乎所有组件一样,SecurityManager的缺省实现是POJO,而且可用POJO兼容的任何配置机制进行配置 - 普通的Java代码、Spring XML、YAML、.properties和.ini文件等。基本来讲,能够实例化类和调用JavaBean兼容方法的任何配置形式都可使用。

    Realms

    Shiro的第三个也是最后一个概念是Realm。Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当切实与像用户帐户这类安全相关数据进行交互,执行认证(登录)和授权(访问控制)时,Shiro会从应用配置的Realm中查找很多内容。

    从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。


    =======配合Spring的webApp安全管理实验===========

    1、创建web工程,导入相应jar包

    另外就是spring的包

    2、修改web.xml

    	<context-param>
    		<param-name>contextConfigLocation</param-name>
    		<!-- Spring配置文件路径 -->
    		<param-value>classpath*:applicationContext.xml</param-value>
    	</context-param>
    
    	<listener>
    		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    	</listener>
    	<filter>
    		<filter-name>shiroFilter</filter-name>
    		<filter-class>
    			org.springframework.web.filter.DelegatingFilterProxy
    		</filter-class>
    	</filter>
    	<filter-mapping>
    		<filter-name>shiroFilter</filter-name>
    		<url-pattern>/*</url-pattern>
    	</filter-mapping>



    3、spring配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    
    	<!-- Shiro Filter 拦截器相关配置 -->
    	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    		<!-- securityManager -->
    		<property name="securityManager" ref="securityManager" />
    		<!-- 登录路径 -->
    		<property name="loginUrl" value="/login.jsp" />
    		<!-- 登录成功后跳转路径 -->
    		<property name="successUrl" value="/index.jsp" />
    		<!-- 未登录跳转路径 -->
    		<property name="unauthorizedUrl" value="/login.jsp" />
    		<!-- 过滤链定义 -->
    		<property name="filterChainDefinitions">
    			<value>
    				/login.jsp* = anon
    				/login.do* = anon
    				/pages/* = authc
    				/index.jsp* = authc
                                   <!-- 访问这些路径必须拥有某种权限
                                   /role/edit/* = perms[role:edit]  
                                   /role/save = perms[role:edit]  
                                   /role/list = perms[role:view]  
                                   -->
                            </value>
    		</property>
    	</bean>
    
    	<!-- securityManager -->
    	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    		<property name="realm" ref="myRealm" />
    	</bean>
    	<!-- 自定义Realm实现 -->
    	<bean id="myRealm" class="javacommon.shiro.CustomRealm" />
    </beans>
    

    过滤链配置了哪些url应该被怎样处理。Shiro内置了不少有用的过滤器:

    Anon:不指定过滤器,不错是这个过滤器是空的,什么都没做,跟没有一样。Authc:验证,这些页面必须验证后才能访问,也就是我们说的登录后才能访问。这里还有其他的过滤器,我没用,比如说授权,这个比较重要,但是这个过滤器有个不好的地方,就是要带一个参数,所以如果配在这里就不是很合适,因为每个页面,或是.do的权限不一样,而我们也没法事先知道他需要什么权限。所以这里不配,我们在代码中再授权。这里.do和.jsp后面的*表示参数,比如login.jsp?main这种,是为了匹配这种。

    过滤器也可以直接配置,而不使用spring为其提供参数,

    <filter>
        <filter-name>ShiroFilter</filter-name>
        <filter-class>
             org.apache.shiro.web.servlet.IniShiroFilter
        </filter-class>
        <!-- 没有init-param属性就表示从classpath:shiro.ini装入INI配置 -->
    </filter>
    <filter-mapping>
        <filter-name>ShiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping> 
    


    4、Realm的实现


    package javacommon.shiro;
    
    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;
    
    public class CustomRealm extends AuthorizingRealm{
    	/**
    	 * 回调函数,提取当事人的角色和权限
    	 * principals  当事人
    	 */
        protected AuthorizationInfo doGetAuthorizationInfo(
                PrincipalCollection principals) {
        	//用户名
            String username = (String) principals.fromRealm(
                    getName()).iterator().next();
           
            /*这些代码应该是动态从数据库中取出的,此处写死*/
            if(username!=null&&username.equals("admin")){
            	SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            	info.addRole("admin");//添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色
            	info.addStringPermission("admin:manage");//添加权限
            	return info;
            }
            return null;
        }
    
       /**
        * 登录验证
        */
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken authcToken ) throws AuthenticationException {
        	//令牌——基于用户名和密码的令牌
            UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
            //令牌中可以取出用户名密码
            String accountName = token.getUsername();
            
           /*此处无需比对,比对的逻辑Shiro会做,我们只需返回一个和令牌相关的正确的验证信息,因此在随后的登录页面上只有admin/admin123才能通过验证*/
            return new SimpleAuthenticationInfo("admin","admin123",getName());
        }
    
    }
    

    5、处理登录

    /login.jsp:

    <body>
    	<h3>${msg }</h3>
    	<form action="LoginServlet" method="post">
    		<br />用户帐号: <input type="text" name="username" id="username" value="" />
    		<br />登录密码: <input type="password" name="password" id="password"
    			value="" /> <br />
    		<input value="登录" type="submit">
    	</form>
    </body>

    LoginServlet代码片段:

    	protected void doPost(HttpServletRequest request,
    			HttpServletResponse response) throws ServletException, IOException {
    		//当前Subject
    		Subject currentUser = SecurityUtils.getSubject();
    		UsernamePasswordToken token = new UsernamePasswordToken(
    				request.getParameter("username"),
    				request.getParameter("password"));
    		token.setRememberMe(true);
    		try {
    			/*
    			 * 在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm
    			 * ,执行必须的认证检查。每个Realm都能在必要时对提交的AuthenticationTokens作出反应。
    			 * 但是如果登录失败了会发生什么?如果用户提供了错误密码又会发生什么?通过对Shiro的运行时AuthenticationException做出反应
    			 * ,你可以控制失败
    			 */
    			currentUser.login(token);
    			request.getRequestDispatcher("/index.jsp").forward(request,
    					response);
    		} catch (AuthenticationException e) {//登录失败
    			e.printStackTrace();
    			request.setAttribute("msg", "不匹配的用户名和密码");
    			request.getRequestDispatcher("/login.jsp").forward(request,
    					response);
    		}
    	}



    6、测试

    直接访问/pages/**.jsp,因为这个是需要认证才能访问的页面,所以,直接访问会被转发到登录页面。

    登录页面输入正确的用户名/密码,将按照LoginServlet的流程来走。

    登录成功后,同一个会话再去访问那些被保护的页面或资源不需重复登录。



    待解决问题:

    权限的判断;

    加密;




  • 相关阅读:
    138.安全退出的异常,要用throw 尽量不用exit(0)
    137.CPP自带异常
    136.异常的多态,父类对象,传递子类的引用或指针(地址)
    135.异常与类继承
    134.异常类的处理
    133.throw机制 抛出类类型
    132.try throw catch介绍
    CF1039D You Are Given a Tree
    CF576E Painting Edges
    【模板】并查集维护生成树
  • 原文地址:https://www.cnblogs.com/riskyer/p/3258255.html
Copyright © 2020-2023  润新知