shiro



对于使用者,主要关注:
subject:主体,代表当前'用户'
SecurityManager:安全管理器,任何与安全相关操作都要与之交互,相当于dispacherServlet
Realm:域,shiro从realm中取安全数据(用户,角色,权限),相当于dataBase



Shiro的其他支持特性

AOP提供AOP方面的支持,实现对某个类某个方法的拦截,从而使权限控制延伸至类的方法。
Cache提供缓存支持
Codec提供编码方面的支持
Crypto提供加密支持
IO从多个资源位置读写原始数据
JNDI提供jndi支持
util工具类支持
标签类用于Web页面

SSM环境下的认证

开发人员:
1、Subject类型的实例:
2、判断当前subject是否认证过
3、Subject login(token):
         UsernamePasswordToken:当前用户输入的值
4、自定义Realm:获取数据库当中的数据

如果比对成功,不会出现任何异常,出现异常会出现认证异常

    shiro API完成用户的认证
    登录Controller中
        1、获取subject类型的实例
        Subject subject = SecurityUtils.getSubject();
   2、判断用户是否登录
    subject.isAuthenticated() 
   3、使用UsernamePasswordToken对象来封装用户名密码
     UsernamePasswordToken token = new UsernamePasswordToken("zhang""123");
   4、使用Subject实例中的login(token) 
    subject.login(token);
   5、Realm:从数据中获取安全数据
  public class MyRealm extends  AuthenticatingRealm{
    @Autowired
    private UserService usersvice;
    /*
     * 1.doGetAuthorizationInfo,获取认证消息,如果数据库中没有数据,返回null,如果得到正确的用户名密码,返回指定类型对象
     *
     * 2.AuthorizationInfo可以使用SimpleAuthenticationInfo实现类封装正确的用户名密码
     *
     * 3.token的参数,就是我们需要认证的token
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken tokenthrows AuthenticationException {
       SimpleAuthenticationInfo info=null;
       // 1.将token转换成UserNamePasswordToken
       UsernamePasswordToken upToken=(UsernamePasswordToken)token;
       //2.获取用户名即可
       String userName=upToken.getUsername();
       //3.查询数据库,是否存在指定用户名密码用户
       User user = usersvice.selectUser(userName);
       if(user!=null){
           //4.如果查询到了,封装查询结果,返回给我们调用
           Object principal=user.getName();
           Object credentials=user.getPassword();
           String realmName=this.getName();
           info=new SimpleAuthenticationInfo(principalcredentialsrealmName);
       }else{
           //5.如果没查到,抛出异常
           throw new AuthenticationException();
       }
       return info;
    }    

  MD5加密
      加密:MD5 SHA1
            密码的对比:通过Realm的credentialsMatcher属性来进行密码的比对!
            1.存储数据的时候,用户输入明文进行加密
            2.前端yoghurt输入的值,进行加密,String类型字符串经过MD5得到一个值
      shiro底层进行密码对比:用户输入值进行MD5加密
       盐值加密:在原有算法基础上,加上唯一的盐值。
    MD5盐值加密:即使两个用户密码一样,加密后的结果也不相同。
  1. doGetAuthenticationInfo方法返回值创建SimpleAuthenticationInfo对象时,需要使用
  2. SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realnName)构造器
  3. 使用ByteSource.Util.bytes()来计算
  4. 盐值需要唯一,如用户名或者随机数
  5. 使用new SimpleHash(hashAlgorithmName ,credentials, salt, hashIterations);来计算盐值加密后密码的值。
ByteSource salt=ByteSource.Util.bytes(userName);
SimpleHash sh= new SimpleHash("MD5", credentials, salt, 1024);

多Realm 
多Realm获取数据:
  意义:多个数据库中获取数据
多个realm,可能加密方式不同,这时需要认证策略, 是一个数据库验证算成功还是多个数据库验证都成功才算成功
第一种方式:
    1.书写多个Realm实现类
    2.SpringIoc容器中完成想要用的bean设置
<bean id="myRealm" class="com.ssm.shiro.MyRealm">
       <!-- 前端输入调用Realm中的credentialsMatcher加密器对密码进行MD5加密1024次。 -->
       <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.ssm.shiro.SecondRealm">
       <!-- 前端输入调用Realm中的credentialsMatcher加密器对密码进行MD5加密1024次。 -->
       <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>
    3.seurityManager的bean中设置一个realms,并且通过集合对象完成属性注入。
      <!-- 安全管理器,配置多个realm -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 
      <property name="realms" > 
       <list>
           <ref bean="myRealm"/>
           <ref bean="secondRealm"/>
       </list>
      </property>
    </bean> 
第二种方式:
1.需要配置一个认证器,ModularRealmAuthenticator
2.bean的realms属性完成注入

<!-- 安全管理器,配置多个realm -->
     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 
    
      <property name="authenticator" ref="authenticator"></property>
    </bean> 
    <!-- 声明一个认证器 -->
    <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
       <property name="realms">
           <list>
               <ref bean="myRealm"/>
               <ref bean="secondRealm"/>
           </list>
       </property>
    </property name="authenticationStrategy" ref="allSuccessfulStrategy"></property>
    </bean>
    <bean id="allSuccessfulStrategy" class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"></bean>

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

修改认证策略
        第二种配置认证器黑色代码部分

授权roles拦截器
授权​:控制哪一个用户可以访问哪一个web资源
    tom-->adimin-->权限的web资源
    jim--->user----->权限的web资源

主体:subject,用户

资源:可以访问的URL

权限:能干什么

角色:包含了多个权限

shiro支持三种方式授权
    --编程式:通过写if/else授权代码块完成
        if(subject.hasRole("admin")){
                }else{
    }
    --注解式:通过在执行java方法上放置相应的注解完成,没有权限将抛出相应异常
        @RequiresRoles("admin")
        public void hello(){
    }
    --JSP/GSP 标签:在JSP/GSP页面通过相应的标签完成
    <shiro:hasRole name="admin">
    </shiro:hasRole>

            默认拦截器
shiro内置了很多默认的拦截器,比如身份验证,授权等相关的。默认拦截器可以参考
    org.apache.shiro.web.filter.mgt.DefaultFilter中的枚举拦截器

                                                授权相关的拦截器
role
org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
角色授权拦截器,验证用户是否拥有所有角色;
主要属性:
longinUrl:登陆页面地址(/login.jsp)
unauthorizedUrl:未授权后重定向的地址;
示例:“/admin/**=roles[admin]"
​perms
org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
权限授权拦截器,验证用户是否拥有所有权限;属性和role一样;
示例:“/user/**=perms["user.create"]”
port
org.apache.shiro.web.filter.authz.PortFilter
端口拦截器,主要属性:port(80) :可以通过的端口;示例“/test=port[80]”
,如果用户访问该页面是非80,将自动将请求端口改为80,重定向
rest
org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
rest风格拦截器,自动根据请求方法构建权限字符串
ssl
org.apache.shiro.web.filter.authz.SslFilter
SSL拦截器,只有请求协议是https才能通过,否则自动跳转会https端口(443);
其他和port拦截器一样

可以通过Ioc容器配置文件完成指定WEB资源对应的角色
filterChainDefinitions

web资源=roles[角色名称]

1.Ioc的配置文件中配置那些角色可以访问那些web资源,roles拦截器,roles[角色 ]
        /admin.jsp=roles[admin]
                -登陆的用户如果拥有admin角色可以访问admin.jsp
2.一个用户进行登陆,认证成功以后,最终还是查找认证成功的用户拥有什么样的角色。
    Realm可以帮助我们与数据库交互,获取认证成功的用户对应的角色。

-自定义一个Realm,继承AuthorizingRealm,认证和授权的工作。
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
       //AuthorizationInfo 返回值封装获取用户对应的所有角色,SimpleAuthorizationInfo(Set<String>)
       //参数列表PrincipalCollection 登陆的身份,即登陆的用户名
       String name=principal.toString();
       SimpleAuthorizationInfo info=null;
       User user = usersvice.selectUser(name);
       if(user!=null){
           Set<String> roles=new HashSet<String>();
           roles.add(user.getRoles());
           info=new SimpleAuthorizationInfo(roles);
       }else{
           //5.如果没查到,抛出异常
           throw new AuthenticationException();
       }
       return info;
    }
-Ioc容器当中配置指定Realm的bean实例
       上面已经定义myRealm
-告知securityManager使用哪一个授权管理器
        配置一个授权管理器   
        <!-- 授权器 -->
    <bean id="authorizer" class="org.apache.shiro.authz.ModularRealmAuthorizer">
        <property name="realms">
           <list>
               <ref bean="myRealm"/>
               <!-- <ref bean="secondRealm"/> -->
           </list>
       </property>
    </bean>
告知sercurityManager,我要用到哪个授权器  
<property name="authorizer" ref="authorizer"></property>


      
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐