Shiro学习笔记(跟Spring整合)


https://download.csdn.net/download/pz0605/10296661
Shrio与Spring的融合步骤:
1,配置ShiroFilterFactoryBean
2. 配置 CacheManager.
2.1 需要加入 ehcache 的 jar 包及配置文件.
3. 配置 Realm
3.1 直接配置实现了 org.apache.shiro.realm.Realm 接口的 bean
4. 配置 LifecycleBeanPostProcessor. 可以自动的来调用配置在 Spring IOC 容器中 shiro bean 的生命周期方法.
5. 启用 IOC 容器中使用 shiro 的注解. 但必须在配置了 LifecycleBeanPostProcessor 之后才可以使用.
6. 配置 ShiroFilter.
6.1 ShiroFilter bean的 id 必须和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致.
    若不一致, 则会抛出: NoSuchBeanDefinitionException. 因为 Shiro 会来 IOC 容器中查找和 <filter-name> 名字对应的 filter bean.

配置web.xml与spring的配置文件时,要特别注意web.xml 中的DelegatingFilterProxy <filter-name> 要与Spring中
ShiroFilterFactoryBean中的bean的id保持一样;

Shrio工作流程:
浏览器 => shiroFilter => 定位到成功或失败页面


Shrio配置受保护的URL:
    ?:匹配一个字符,如 /admin? 将匹配 /admin1,但不 匹配 /admin 或 /admin/;
    *:匹配零个或多个字符串,如 /admin 将匹配 /admin、 /admin123,但不匹配 /admin/1;
    **:匹配路径中的零个或多个路径,如 /admin/** 将匹配 /admin/a 或 /admin/a/b

    URL匹配原则:第一次匹配优先,所以越是模糊的匹配就该越放到后边


Shrio认证(登陆)流程:

   1. 获取当前的 Subject. 调用 SecurityUtils.getSubject();
   2. 测试当前的用户是否已经被认证. 即是否已经登录. 调用 Subject 的 isAuthenticated()
   3. 若没有被认证, 则把用户名和密码封装为 UsernamePasswordToken 对象

   如何从数据库中获取用户记录?使用Realm,具体步骤如下:
   1). 创建一个表单页面
   2). 把表单的用户名与密码请求提交到 SpringMVC 的 Handler
   3). 在Handler中获取用户名和密码.
   4). 执行登录: 调用 Subject 的 login(AuthenticationToken) 方法.
   5). 自定义 Realm 的方法, 从数据库中获取对应的记录, 返回给 Shiro.
     如何自定义Realm
     5.1). 实际上需要继承 org.apache.shiro.realm.AuthenticatingRealm 类
     5.2). 实现AuthorizingRealm接口中doGetAuthenticationInfo(AuthenticationToken) 方法.
   6. 由 shiro 完成对密码的比对.

    org.apache.shiro.subject.support.DelegatingSubject.login()

自定义的AuthorizingRealm主要逻辑:
   1,实现doGetAuthenticationInfo(AuthenticationToken token)用于认证
   2,把 AuthenticationToken 转换为 UsernamePasswordToken
      UsernamePasswordCaptchaToken token = (UsernamePasswordCaptchaToken)authcToken;
   3,从数据库中获取用户信息
      User user = this.userService.findUserByLoginName(token.getUsername());
   4,判断用户是否存在、账号是否禁用等
   5,根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
      以下信息是从数据库中获取的.
     1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象.
     2). credentials: 密码.
     3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
     4). 盐值加密.

Shrio密码比对:
通过 AuthenticatingRealm 的 credentialsMatcher.doCredentialsMatch 方法来进行的密码的比对!

Shrio MD5 盐值加密,为什么使用 MD5 盐值加密?
如何实现MD5加密:
 1). 在 doGetAuthenticationInfo 方法返回值创建 SimpleAuthenticationInfo 对象的时候, 需要使用
 SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName) 构造器
 2). 使用 ByteSource.Util.bytes() 来计算盐值.
 3). 盐值需要唯一: 一般使用随机字符串或 user id
 4). 使用 new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations); 来计算盐值加密后的密码的值.


Shrio多Realm验证:
   1,配置ModularRealmAuthenticator
       <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
           <property name="authenticationStrategy">
               <bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy"/>
           </property>
       </bean>
   2,配置realms
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <property name="authenticator" ref="authenticator"></property>
        <property name="realms">
           <list>
             <ref bean="jdbcRealm"/>
             <ref bean="secondRealm"/>
          </list>
        </property>
        <property name="rememberMeManager.cookie.maxAge" value="10"></property>
    </bean>

    注意:realms中list有先后顺序

多个Realm的认证策略:
    ModularRealmAuthenticator.doMultiRealmAuthentication
    FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第 一个 Realm 身份验证成功的认证信息,其他的忽略;
    AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和 FirstSuccessfulStrategy 不同,将返回所有Realm身份验证成功的认证信 息;
    AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有 Realm身份验证成功的认证信息,如果有一个失败就失败了。
    ModularRealmAuthenticator 默认是 AtLeastOneSuccessfulStrategy 策略, 默认的认证策略有一个就OK了

认证策略的配置:
    <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
        <property name="authenticationStrategy">
             <bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy"/><!--第一个认证成功即可-->
        </property>
    </bean>

Realm授权:
   也叫访问控制,即在应用中控制谁访问哪些资源(如访问页面/编辑数据/页面操作 等)。
   在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限 (Permission)、角色(Role)。
   三种授权方式:
   编程式:
   注解式:
   JSP/GSP标签:

   默认拦截器可以参考:org.apache.shiro.web.filter.mgt.DefaultFilter
   或 http://blog.csdn.net/sunqingzhong44/article/details/68961199


授权的Realm如何实现?
   1. 授权需要继承 AuthorizingRealm 类, 并实现其 doGetAuthorizationInfo 方法
   2. AuthorizingRealm 类继承自 AuthenticatingRealm, 但没有实现 AuthenticatingRealm 中的
    doGetAuthenticationInfo, 所以认证和授权只需要继承 AuthorizingRealm 就可以了. 同时实现他的两个抽象方法.

  参考代码:
   //授权会被 shiro 回调的方法
   @Override
   protected AuthorizationInfo doGetAuthorizationInfo(
         PrincipalCollection principals) {
      //1. 从 PrincipalCollection 中来获取登录用户的信息
      Object principal = principals.getPrimaryPrincipal();

      //2. 利用登录的用户的信息来用户当前用户的角色或权限(可能需要查询数据库)
      Set<String> roles = new HashSet<>();
      roles.add("user");
      if("admin".equals(principal)){
         roles.add("admin");
      }

      //3. 创建 SimpleAuthorizationInfo, 并设置其 reles 属性.
      SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);

      //4. 返回 SimpleAuthorizationInfo 对象.
      return info;
   }


多Realm的授权的时候,怎么才算授权通过?只要有一个通过就算通过!


Shire标签:
    guest 标签:用户没有身份验证时显示相应信息,即游客 访问信息:
    user 标签:用户已经经过认证/记住我登录后显示相应的信 息。
    authenticated 标签:用户已经身份验证通过,即Subject.login登录成功,不是记住我登录的
    notAuthenticated 标签:用户未进行身份验证,即没有调 用Subject.login进行登录,包括记住我自动登录的也属于 未进行身份验证。
    pincipal 标签:显示用户身份信息,默认调用 Subject.getPrincipal() 获取,即 Primary Principal。
    hasRole 标签:如果当前 Subject 有角色将显示 body 体内 容:
    hasAnyRoles 标签:如果当前Subject有任意一个 角色(或的关系)将显示body体内容。
    lacksRole:如果当前 Subject 没有角色将显 示 body 体内容
    hasPermission:如果当前 Subject 有权限 将显示 body 体内容
    lacksPermission:如果当前Subject没有权 限将显示body体内容。

    使用的时候要导入:<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

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。

   比如:
   public class ShiroService {
          @RequiresRoles({"admin"})
       public void testMethod(){
             System.out.println("testMethod, time: " + new Date());
             Session session = SecurityUtils.getSubject().getSession();
             Object val = session.getAttribute("key");
             System.out.println("Service SessionVal: " + val);
       }
   }

   在控制器中:
   @RequestMapping("/testShiroAnnotation")
   public String testShiroAnnotation(HttpSession session){
       session.setAttribute("key", "value12345");
          shiroService.testMethod();//没有admin权限,将会抛出错误会
          return "redirect:/list.jsp";
   }

Shiro资源与权限从数据库中获取:
   1,配置一个 bean, 该 bean 实际上是一个 Map. 通过实例工厂方法的方式 -->
     <bean id="filterChainDefinitionMap"
       factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap"></bean>
     <bean id="filterChainDefinitionMapBuilder"
          class="com.atguigu.shiro.factory.FilterChainDefinitionMapBuilder"></bean>
     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login.jsp"/>
        <property name="successUrl" value="/list.jsp"/>
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
     </bean>

   2,
   public class FilterChainDefinitionMapBuilder {
    //从数据库中加载资源与权限
   public LinkedHashMap<String, String> buildFilterChainDefinitionMap(){
      LinkedHashMap<String, String> map = new LinkedHashMap<>();
      map.put("/login.jsp", "anon");
      map.put("/shiro/login", "anon");
      map.put("/shiro/logout", "logout");
      map.put("/user.jsp", "authc,roles[user]");
      map.put("/admin.jsp", "authc,roles[admin]");
      map.put("/list.jsp", "user");
      map.put("/**", "authc");
      return map;
   }
  }


Shiro会话管理:
   会话相关API:
   Subject.getSession():即可获取会话;其等价于 Subject.getSession(true),
            即如果当前没有创建 Session 对象会创建 一个;Subject.getSession(false),
            如果当前没有创建 Session 则返回 null
   session.getId():获取当前会话的唯一标识
   session.getHost():获取当前Subject的主机地址
   session.getTimeout() & session.setTimeout(毫秒):获取/设置当 前Session的过期时间
   session.getStartTimestamp() & session.getLastAccessTime(): 获取会话的启动时间及最后访问时间;
           如果是 JavaSE 应用需要自己定 期调用 session.touch() 去更新最后访问时间;如果是 Web 应用,
           每 次进入 ShiroFilter 都会自动调用 session.touch() 来更新最后访问时间


Shiro缓存:
      shiro 内部相应的组件(DefaultSecurityManager)会自 动检测相应的对象(如Realm)是否实现了
      CacheManagerAware 并自动注入相应的 CacheManager

认证和记住我
   subject.isAuthenticated() 表示用户进行了身份验证登录的,即使有 Subject.login 进行了登录;
   subject.isRemembered():表示用户是通过记住我登录的, 此时可能并不是真正的你(如你的朋友使用你的电脑,或者 你的cookie 被窃取)在访问的
   两者二选一,即 subject.isAuthenticated()==true,则 subject.isRemembered()==false;反之一样。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值