安全框架 shiro 框架学习

    Shiro是Apache的一个安全框架. 可以完成 认证 授权 加密 会话管理 与web集成  缓存等.

(1)基本功能点:

Authentication: 身份认证/登录,验证用户是不是拥有相应的身份;
Authorization: 授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用
                          户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户
                          对某个资源是否具有某个权限;
Session Manager: 会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中; 会话可以是普通                                         JavaSE 环境, 也可以是 Web 环境的;
Cryptography: 加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support: Web 支持,可以非常容易的集成到Web 环境;
Caching: 缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可 以提高效率;
Concurrency: Shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As: 允许一个用户假装为另一个用户(如果他们允许) 的身份进行访问;
Remember Me: 记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了


(2)shiro架构   

• Subject: 应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外API 核心就是 Subject。 Subject 代表了当前“用户” ,          这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等; 与 Subject 的所有交          互都会委托给 SecurityManager;Subject 其实是一个门面, SecurityManager 才是实际的执行者;
• SecurityManager: 安全管理器;即所有与安全有关的操作都会与SecurityManager 交互;且其管理着所有 Subject;可以看出它是 Shiro的核心,它负责与 Shiro 的其他组件进行交互,它相当于 SpringMVC 中DispatcherServlet 的角色
• Realm: Shiro 从 Realm 获取安全数据(如用户、角色、权限), 就是说SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色/权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource.

(3)HelloWorld程序

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Quickstart {

    private static final transient Logger log =LoggerFactory.getLogger(Quickstart.class);


    public static void main(String[] args) {

        //认证工厂
        Factory<SecurityManager> factory = new 
                            IniSecurityManagerFactory("classpath:shiro.ini");
        //认证管理器
        SecurityManager securityManager = factory.getInstance();

        //
        SecurityUtils.setSecurityManager(securityManager);

        //获取Subject,用于交互
        Subject currentUser = SecurityUtils.getSubject();

        //操作session
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
  
        // 测试当前的用户是否已经被认证. 即是否已经登录. 
        // 调动 Subject 的 isAuthenticated() 
        if (!currentUser.isAuthenticated()) {

        	//Token  把用户名和密码封装为 UsernamePasswordToken 对象
            UsernamePasswordToken token = new UsernamePasswordToken("q", "vespa");
            token.setRememberMe(true);

            try {
            	// 执行登录. 
                currentUser.login(token);
            } 
            // 若没有指定的账户, 则 shiro 将会抛出 UnknownAccountException 异常. 
            catch (UnknownAccountException uae) {
                log.info("There is no user with username of " + token.getPrincipal());
                return; 
            } 
            // 若账户存在, 但密码不匹配, 则shiro会抛出 IncorrectCredentialsException 异常。 
            catch (IncorrectCredentialsException ice) {
                log.info("Password for account  was incorrect!");
                return; 
            } 
            // 用户被锁定的异常 LockedAccountException
            catch (LockedAccountException lae) {
                log.info("The account for username is locked.  " +
                        "Please contact your administrator to unlock it.");
            }
            // 所有认证时异常的父类. 
            catch (AuthenticationException ae) {
                //unexpected condition?  error?
            }
        }

        //print their identifying principal (in this case, a username):
        log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

        //test a role:
        // 测试是否有某一个角色. 调用 Subject 的 hasRole 方法. 
        if (currentUser.hasRole("schwartz")) {
            log.info("----> May the Schwartz be with you!");
        } else {
            log.info("----> Hello, mere mortal.");
            return; 
        }

        // 测试用户是否具备某一个行为. 调用 Subject 的 isPermitted() 方法。 
        if (currentUser.isPermitted("lightsaber:weild")) {
            log.info("----> You may use a lightsaber ring.  Use it wisely.");
        } else {
            log.info("Sorry, lightsaber rings are for schwartz masters only.");
        }

        // 测试用户是否具备某一个行为. 
        if (currentUser.isPermitted("user:delete:zhangsan")) {
            //具备权限
        } else {
            //不具备
        }

        // 执行登出. 调用 Subject 的 Logout() 方法. 
        System.out.println("---->" + currentUser.isAuthenticated());
        
        currentUser.logout();      //执行登出操作
        
        System.out.println("---->" + currentUser.isAuthenticated());

        System.exit(0);
    }
}

(4) web环境下整合Shiro

applicationContext.xml配置      其中jdbcrealm需要自定义并实现AuthorizingRealm,以下配置是单realm的配置

<?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.0.xsd">

    <!-- securityManager核心管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <property name="realm" ref="jdbcRealm"/>
    </bean>

    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
    </bean>

   <!-- 多Realm的配置 -->
   <bean id="jdbcRealm" class="com.atguigu.shiro.realms.ShiroRealm">
    	<property name="credentialsMatcher">
    		<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!-- 加密方式 -->
    			<property name="hashAlgorithmName" value="MD5"></property>
                <!-- 定义加密次数 -->
    			<property name="hashIterations" value="1024"></property>
    		</bean>
    	</property>
    </bean>
    
    <bean id="secondRealm" class="com.atguigu.shiro.realms.SecondRealm">
    	<property name="credentialsMatcher">
    		<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
    			<property name="hashAlgorithmName" value="SHA1"></property>
    			<property name="hashIterations" value="1024"></property>
    		</bean>
    	</property>
    </bean>



    <!-- 让Spring IOC管理bean的生命周期 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!-- 开启注解,需要依赖lifecycleBeanPostProcessor -->
    <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>

    <!-- shiroFilter与web.xml配置的名称一样,否则会找不到,也可以在web.xml中配置tagname -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/s/login"/>
        <property name="successUrl" value="/s/index"/>
        <property name="unauthorizedUrl" value="/s/unauthorized"/>
      
        <property name="filterChainDefinitions">
            <value>
                /favicon.ico = anon      #匿名访问
                /logo.png = anon
                /shiro.css = anon
                /s/login = anon
                # allow WebStart to pull the jars for the swing app:
                /*.jar = anon
                # everything else requires authentication:
                /** = authc              #必须授权才能访问 
            </value>
        </property>
    </bean>

</beans>

web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	
	<!-- web容器启动加载Spring的IOC容器 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<!-- SpringMVC的核心控制器 -->
	<servlet>
		<servlet-name>spring</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>spring</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!-- 
	1. 配置  Shiro 的 shiroFilter.  
	2. DelegatingFilterProxy 实际上是 Filter 的一个代理对象. 默认情况下, Spring 会到 IOC 容            
       器中查找和 
	   <filter-name> 对应的 filter bean. 也可以通过 targetBeanName 的初始化参数来配置 filter 
        bean 的 id. 
	-->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
	
</web-app>

实现AuthorizingRealm的接口  
@Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {

        ByteSource credentialsSalt = ByteSource.Util.bytes(username);   //密码加盐操作
        SimpleAuthenticationInfo info = null; //new SimpleAuthenticationInfo(principal, credentials, realmName);

       // principal 用户名或者用户的实体    credentials  密码     realmName  不重要随便写
        info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);  //credentialsSalt  盐值

}

(5)认证策略

<bean id="authenticator" 
    	class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">

        <!-- 认证策略 三种 
            • FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第
                    一个 Realm 身份验证成功的认证信息,其他的忽略;
            • AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可, 和
                    FirstSuccessfulStrategy 不同,将返回所有Realm身份验证成功的认证信息;
            • AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有
                    Realm身份验证成功的认证信息,如果有一个失败就失败了。
        -->
    	<property name="authenticationStrategy">
    		<bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean>
    	</property>
</bean>

*******在authoticator中可以配置realms属性指向多个realm, 也可以在securityManager中配置realms属性,但是推荐后者,在授权时可以使用.

(6) 授权   

     Shiro三种授权的方式:  1)编程式   2)注解式     3)jsp标签

     /admin.jsp = roles[admin]   # 需要admin相关的角色,否则将进入到未授权的页面

     authotication (只是认证)-------->   authorizingRealm(认证和授权)

@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		//1. 从当前的subject中获取用户的信息
		Object principal = principals.getPrimaryPrincipal();
		
		//2. 获取用户的角色信息
                // 在shirofilter时的配置   /admin.jsp = roles[admin] /user.jsp = roles[user]
		Set<String> roles = new HashSet<>();
		roles.add("user");
		if("admin".equals(principal)){
			roles.add("admin");  //对于admin的用户同时拥有两个角色的信息
		}
		
		//3. 为当前的认证信息封装角色信息
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
		
		//4. 返回信息
		return info;
	}

(7)shiro的标签

<shiro:principal></shiro:principal>   当前认证过的用户信息

<shiro:hasrole name='admin'>********</shiro:hasrole>

(8)shiro的权限注解

@RequiresAuthentication:表示当前Subject已经通过login进行了身份验证; 即 Subject. isAuthenticated() 返回 true
@RequiresUser:表示当前 Subject 已经身份验证或者通过记住我登录的。
@RequiresGuest:表示当前Subject没有身份验证或通过记住我登录过,即是游客身份。
@RequiresRoles(value={“admin”, “user”}, logical=Logical.AND):表示当前 Subject 需要角色 admin 和user
@RequiresPermissions (value={“user:a”, “user:b”},logical= Logical.OR):表示当前 Subject 需要权限 user:a 或user:b。
开发时配置在Controller中

(9)认证和授权的另外一种配置方式,linkedHashmap 封装连接地址

    <!-- 配置一个 bean, 该 bean 实际上是一个 Map. 通过实例工厂方法的方式 -->
    <bean id="filterChainDefinitionMap" 
    	factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap"></bean>
    
    <bean id="filterChainDefinitionMapBuilder"
    	class="com.atguigu.shiro.factory.FilterChainDefinitionMapBuilder">
    </bean>

(10)shiro的会话管理  不依赖于web容器

(11)shiro记住我

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值