shiro学习1

使用Shiro
可以通过下述代码获得当前正在执行程序的用户

Subject currentUser = SecurityUtils.getSubject();

使用SecurityUtils.getSubject方法,我们可以获得当前正在执行程序的Subject。我们并不称之为前正在执行程序的用户,因为用户通常是指人,而Subject可以指人、进程、计划任务、守护进程等。准确的说,Subject指的是“当前和软件交互的事物”。在多数场景中,你可以将Subject粗暴地认为是用户

//测试用例,shiro.ini放在resources下 ,添加如下代码

[users]  
zhang=123  
wang=123

身份验证的步骤:
==1、收集用户身份/凭证,即如用户名/密码;
2、调用Subject.login进行登录,如果失败将得到相应的AuthenticationException异常,根据异常提示用户错误信息;否则登录成功;
3、最后调用Subject.logout进行退出操作。==

@Test  
public void testHelloworld() {  
    //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager  
    Factory factory =  
            new IniSecurityManagerFactory("classpath:shiro.ini");  
    //2、得到SecurityManager实例 并绑定给SecurityUtils  
    org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();  
    SecurityUtils.setSecurityManager(securityManager);  
    //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)  
    Subject subject = SecurityUtils.getSubject();  
    UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");  

    try {  
        //4、登录,即身份验证  
        subject.login(token);  
    } catch (AuthenticationException e) {  
        //5、身份验证失败  
    }  

    Assert.assertEquals(true, subject.isAuthenticated()); //断言用户已经登录  

    //6、退出  
    subject.logout();  
} 

shiro的身份认证流程
==1、首先调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils. setSecurityManager()设置;
2、SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;
3、Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;
4、Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;
5、Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问==。

Realm(安全数据源)
Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),
就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;
也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;
可以把Realm看成DataSource,即安全数据源

org.apache.shiro.realm.Realm接口
    String getName(); //返回一个唯一的Realm名字  
    boolean supports(AuthenticationToken token); //判断此Realm是否支持此Token  
    AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)  
    throws AuthenticationException;  //根据Token获取认证信息  

验证用户账号的Authenticator(Shiro API中身份验证核心的入口点)
SecurityManager接口继承了Authenticator,另外还有一个ModularRealmAuthenticator实现,其委托给多个Realm进行验证,验证规则通过AuthenticationStrategy接口指定,默认提供的实现:
FirstSuccessfulStrategy:只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他的忽略;
AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy不同,返回所有Realm身份验证成功的认证信息;
AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有Realm身份验证成==mark text==功的认证信息,如果有一个失败就失败了。
AtLeastOneSuccessfulStrategy和FirstSuccessfulStrategy的区别
一个是返回所有验证成功的Realm的认证信息;另一个是只返回第一个验证成功的Realm的认证信息

shiro权限
根对象SecurityManager
Shiro是从根对象SecurityManager进行身份验证和授权的;也就是所有操作都是自它开始的,这个对象是线程安全且真个应用只需要一个即可,因此Shiro提供了SecurityUtils让我们绑定它为全局的,方便后续操作。
因为Shiro的类都是POJO的,因此都很容易放到任何IoC容器管理。但是和一般的IoC容器的区别在于,Shiro从根对象securityManager开始导航;
Shiro支持的依赖注入:public空参构造器对象的创建、setter依赖注入。
散列算法

散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,
常见的散列算法如MD5、SHA等。一般进行散列时最好提供一个salt(盐),比如加密密码“admin”,产生的散列值是“21232f297a57a5a743894a0e4a801fc3”,可以到一些md5解密网站很容易的通过散列值得到密码“admin”,
即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,如用户名和ID(即盐);
这样散列的对象是“密码+用户名+ID”,这样生成的散列值相对来说更难破解。

 String str = "hello";  
    String salt = "123";  
    String md5 = new Md5Hash(str, salt).toString();//还可以转换为 toBase64()/toHex() 

为了方便使用,Shiro提供了HashService,默认提供了DefaultHashService实现:

 DefaultHashService hashService = new DefaultHashService(); //默认算法SHA-512  
    hashService.setHashAlgorithmName("SHA-512");  
    hashService.setPrivateSalt(new SimpleByteSource("123")); //私盐,默认无  
    hashService.setGeneratePublicSalt(true);//是否生成公盐,默认false  
    hashService.setRandomNumberGenerator(new SecureRandomNumberGenerator());//用于生成公盐。默认就这个  
    hashService.setHashIterations(1); //生成Hash值的迭代次数  

    HashRequest request = new HashRequest.Builder()  
                .setAlgorithmName("MD5").setSource(ByteSource.Util.bytes("hello"))  
                .setSalt(ByteSource.Util.bytes("123")).setIterations(2).build();  
    String hex = hashService.computeHash(request).toHex();

==1、首先创建一个DefaultHashService,默认使用SHA-512算法;
2、可以通过hashAlgorithmName属性修改算法;
3、可以通过privateSalt设置一个私盐,其在散列时自动与用户传入的公盐混合产生一个新盐;
4、可以通过generatePublicSalt属性在用户没有传入公盐的情况下是否生成公盐;
5、可以设置randomNumberGenerator用于生成公盐;
6、可以设置hashIterations属性来修改默认加密迭代次数;
7、需要构建一个HashRequest,传入算法、数据、公盐、迭代次数==
加密与解密:Shiro还提供对称式加密/解密算法的支持,如AES、Blowfish等

 //aes算法
    AesCipherService aesCipherService = new AesCipherService();  
    aesCipherService.setKeySize(128); //设置key长度  
    //生成key  
    Key key = aesCipherService.generateNewKey();  
    String text = "hello";  
    //加密  
    String encrptText =   
    aesCipherService.encrypt(text.getBytes(), key.getEncoded()).toHex();  
    //解密  
    String text2 =  
     new String(aesCipherService.decrypt(Hex.decode(encrptText), key.getEncoded()).getBytes());  
    Assert.assertEquals(text, text2);   

HashedCredentialsMatcher实现密码验证服务

==Shiro提供了CredentialsMatcher的散列实现HashedCredentialsMatcher,和之前的PasswordMatcher不同的是,它只用于密码验证,且可以提供自己的盐,而不是随机生成盐,且生成密码散列值的算法需要自己写,因为能提供自己的盐==
散列算法是:md5(md5(密码+username+salt2))

 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {  
        String username = "liu"; //用户名及salt1  
        String password = "202cb962ac59075b964b07152d234b70"; //加密后的密码  
        String salt2 = "202cb962ac59075b964b07152d234b70";  
    SimpleAuthenticationInfo ai =   
            new SimpleAuthenticationInfo(username, password, getName());  
        ai.setCredentialsSalt(ByteSource.Util.bytes(username+salt2)); //盐是用户名+随机数  
            return ai;  
    }   

Shiro内置了很多默认的拦截器,**比如身份验证、授权等相关的。
默认拦截器可以参考org.apache.shiro.web.filter.mgt.DefaultFilter中的枚举拦截器**
org.apache.shiro.web.filter.authc.FormAuthenticationFilter
基于表单的拦截器;如“/**=authc”,如果没有登录会跳到相应的登录页面登录
==主要属性:usernameParam:表单提交的用户名参数名( username);
passwordParam;表单提交的密码参数名(password);rememberMeParam:表单提交的密码参数名(rememberMe);
loginUrl:登录页面地址(/login.jsp);
successUrl:登录成功后的默认重定向地址; failureKeyAttribute:登录失败后错误信息存储key(shiroLoginFailure);==

org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
Basic HTTP身份验证拦截器,主要属性: applicationName:弹出登录框显示的信息(application);

org.apache.shiro.web.filter.authc.LogoutFilter
退出拦截器,主要属性:redirectUrl:退出成功后重定向的地址(/);示例“/logout=logout”

org.apache.shiro.web.filter.authc.UserFilter
用户拦截器,用户已经身份验证/记住我登录的都可;示例“/**=user”

org.apache.shiro.web.filter.authc.AnonymousFilter
匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤;示例“/static/**=anon”
Shiro提供了JSTL标签用于在JSP/GSP页面进行权限控制,如根据登录用户显示相应的页面按钮
导入标签库 <%@taglib prefix=”shiro” uri=”http://shiro.apache.org/tags” %>

Shiro提供了记住我(RememberMe)的功能,比如访问如淘宝等一些网站时,关闭了浏览器下次再打开时还是能记住你是谁,下次访问时无需再登录即可访问,基本流程如下
**1、首先在登录页面选中RememberMe然后登录成功;如果是浏览器登录,一般会把RememberMe的Cookie写到客户端并保存下来;
2、关闭浏览器再重新打开;会发现浏览器还是记住你的;
3、访问一般的网页服务器端还是知道你是谁,且能正常访问
4、但是比如我们访问淘宝时,如果要查看我的订单或进行支付时,此时还是需要再进行身份认证的,以确保当前用户还是你**
springmvc.xml的配置需要注意的地方:
context:component-scan扫描使用上的容易忽视的use-default-filters
下面这只会扫描到@Controller注解的bean,不会扫描@Service/@Repository的Bean

     <context:component-scan base-package="org.bdp.system.test.controller">   
   //<context:component-scan base-package="org.bdp">   会扫描@Service/@Repository的Bean
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   
     context:component-scan>  

如果此处还加载dao层或service层的bean会将之前容器加载的替换掉,而且此处不会进行AOP织入,所以会造成AOP失效问题(如事务不起作用)
shiro登录踢出实例:

id="kickoutSessionControlFilter"   
class="com.github.zhangkaitao.shiro.chapter18.web.shiro.filter.KickoutSessionControlFilter">  
    <property name="cacheManager" ref="cacheManager"/>  
    <property name="sessionManager" ref="sessionManager"/>  

    <property name="kickoutAfter" value="false"/>  
    <property name="maxSession" value="2"/>  
    <property name="kickoutUrl" value="/login?kickout=1"/>  
  

==cacheManager:使用cacheManager获取相应的cache来缓存用户登录的会话;用于保存用户—会话之间的关系的;
sessionManager:用于根据会话ID,获取会话进行踢出操作的;
kickoutAfter:是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;
maxSession:同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录;
kickoutUrl:被踢出后重定向到的地址==;

shiro与spring的合成:


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       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.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">


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

    
    <bean id="credentialsMatcher" class="com.github.zhangkaitao.shiro.chapter13.credentials.RetryLimitHashedCredentialsMatcher">
        <constructor-arg ref="cacheManager"/>
        <property name="hashAlgorithmName" value="md5"/>
        <property name="hashIterations" value="2"/>
        <property name="storedCredentialsHexEncoded" value="true"/>
    bean>
    
    <bean id="userRealm" class="com.github.zhangkaitao.shiro.chapter13.realm.UserRealm">
        <property name="userService" ref="userService"/>
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
        <property name="cachingEnabled" value="true"/>
        <property name="authenticationCachingEnabled" value="true"/>
        <property name="authenticationCacheName" value="authenticationCache"/>
        <property name="authorizationCachingEnabled" value="true"/>
        <property name="authorizationCacheName" value="authorizationCache"/>
    bean>
    
    <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>

    
    <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg value="sid"/>
        <property name="httpOnly" value="true"/>
        <property name="maxAge" value="-1"/>
    bean>
     <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg value="rememberMe"/>
        <property name="httpOnly" value="true"/>
        <property name="maxAge" value="2592000"/>
    bean>

    
    <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        
        <property name="cipherKey"
                  value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/>
        <property name="cookie" ref="rememberMeCookie"/>
    bean>

     
    <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
        <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
        <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
    bean>
    
    <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
        <property name="sessionValidationInterval" value="1800000"/>
        <property name="sessionManager" ref="sessionManager"/>
    bean>

    
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <property name="globalSessionTimeout" value="1800000"/>
        <property name="deleteInvalidSessions" value="true"/>
        <property name="sessionValidationSchedulerEnabled" value="true"/>
        <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
        <property name="sessionDAO" ref="sessionDAO"/>
        <property name="sessionIdCookieEnabled" value="true"/>
        <property name="sessionIdCookie" ref="sessionIdCookie"/>
    bean>

    
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="userRealm"/>
        <property name="sessionManager" ref="sessionManager"/>
        <property name="cacheManager" ref="cacheManager"/>
        <property name="rememberMeManager" ref="rememberMeManager"/>
    bean>

    
    <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="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
        <property name="usernameParam" value="username"/>
        <property name="passwordParam" value="password"/>
        <property name="rememberMeParam" value="rememberMe"/>
        <property name="loginUrl" value="/login.jsp"/>
    bean>

    
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login.jsp"/>
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
        <property name="filters">
            <util:map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            util:map>
        property>
        <property name="filterChainDefinitions">
            <value>
                /login.jsp = authc
                /logout = logout
                /authenticated.jsp = authc
                /** = user
            value>
        property>
    bean>

    
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

beans>

你可能感兴趣的:(shiro教程)