Shiro验证策略

Authentication Strategy:认证策略,在shiro中有三种认证策略;

AtleastOneSuccessfulStrategy【默认】

如果一个或多个Realm验证成功,则整体的尝试被认为是成功的。如果没有一个验证成功,则整体尝试失败;

FirstSuccessfulStrategy

只有第一个成功地验证的Realm返回的信息将被使用。所有进一步的Realm将被忽略。如果没有一个验证成功,则整体尝试失败。

AllSuccessfulStrategy

为了整体的尝试成功,所有配置的Realm必须验证成功。如果没有一个验证成功,则整体尝试失败

authenticationStrategy=org.apache.shrio.authc.pam.FirstSuccessfulStrategy

securityManager.authentication.authenticationStrategy=$authenticationStrategy

[main]

dataSource=com.mchange.v2.c3p0.ComboPooledDataSource

dataSource.driverClass=com.mysql.jdbc.Driver

dataSource.jdbcUrl=jdbc:mysql://localhost:3306/myshiro

dataSource.user=root

dataSource.password=1234567

jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm

#$表示引用对象

jdbcRealm.dataSource=$dataSource

#配置验证器

authenticationStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy

securityManager.realm=$jdbcRealm

securityManager.authenticator.authenticationStrategy=$authenticationStrategy

自定义实现realm来实现身份认证

JdbcRealm已经实现了从数据库中获取用户的验证信息,但是jdbcRealm的灵活性太差。如果要实现自己的一些特殊应用时将不能支持。这时候我们可以通过自定义realm来实现身份认证的功能。

   Realm是一个接口,在接口中定义了根据token获得认证信息的办法。Shiro内容实现了一系列的realm。这些不同Realm实现类提供了不同的功能。AuthenticatingRealm实现了获取身份信息的功能,AuthoringRealm实现了权限信息的功能。通常自定义Realm需要继承AuthoringRealm,这样既可以提供身份认证的自由定义方法也可以实现授权的自定义方法。

publicclass UserRealm extends AuthorizingRealm{

 

@Override

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection token) {

// TODO Auto-generated method stub

       String username = (String)token.getPrimaryPrincipal();

       String pwd = "1111";//相当于从数据库中取得

       SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,pwd,getName());

       return (AuthorizationInfo) info;

    }

 

@Override

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {

// TODO Auto-generated method stub

       returnnull;

    }

   

}

Shiro.ini

[main]

userRealm=com.pshdhx.UserRealm

securityManager.realm=$userRealm

注意:使用shiro来完成权限管理,shiro并不会去维护数据。Shiro中使用的数据,需要程序员根据处理业务将数据传递给给shiro的相关接口;

散列算法(加密)

1、在身份认证过程中往往会涉及加密。如果不加密那么数据信息不安全。Shiro内容实现了比较多的散列算法。如MD5加密,SHA等。并且提供了加盐功能。比如1111的md5加密是确定的“b59c67bf196a4758191e42f76670ceba”==直接在navicat中查询select MD5('1111')即可得到。这个md5加密很容易被破解,但是如果1111+姓名,那么找到原密码的难度会增加。

package com.pshdhx;

 

import org.apache.shiro.crypto.hash.Md5Hash;

 

publicclass MD5Demo {

    publicstaticvoid main(String[] args) {

//普通md5加密1111

       Md5Hash md5 = new Md5Hash("1111");

       System.out.println(md5.toString());

//盐加密

md5 = new Md5Hash("1111", "com.pshdhx");

       System.out.println(md5.toString());

//盐加密+迭代次数

md5 = new Md5Hash("1111", "com.pshdhx", 2);

       System.out.println(md5.toString());

    }

}

b59c67bf196a4758191e42f76670ceba

799367c240adf5478aa2c2a1c081b0bd

458563a4756175ca15176053ca82711c

 

 

 

授权

授权:给通过身份认证的人,授予他可以访问某些资源的权限;

权限粒度:粗粒度:对表的curd;细粒度:是对记录的操作。如只允许查看id为1的一条记录。Shiro一般管理的是粗粒度的权限。比如:菜单,按钮,url。一般细粒度的权限是通过业务来控制的。

角色:权限的集合。管理授权方便。

权限表示规则:资源:操作:实例。可以用通配符表示:

  如user:add  表示对user有添加的权限

User:*  表示对user有所有的权限;

User:delete:100  表示对user标识为100的实例具有删除的权限;

 

package com.pshdhx;

 

import java.util.Arrays;

 

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.AuthenticationException;

import org.apache.shiro.authc.UsernamePasswordToken;

import org.apache.shiro.authz.AuthorizationException;

import org.apache.shiro.config.IniSecurityManagerFactory;

import org.apache.shiro.mgt.SecurityManager;

import org.apache.shiro.subject.Subject;

import org.apache.shiro.util.Factory;

 

publicclass AuthorizationDemo {

    publicstaticvoid main(String[] args) throws Exception {

       Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");

       SecurityManager securityManager = factory.getInstance();

       SecurityUtils.setSecurityManager(securityManager);

       Subject subject = SecurityUtils.getSubject();

       UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "1111");

       try {

subject.login(token);

       } catch (AuthenticationException e) {

// TODO Auto-generated catch block

e.printStackTrace();

           System.out.println("认证不通过");

       }

      

       boolean hasRole = subject.hasRole("role1");

      

       System.out.println("role1的存在==="+hasRole);

      

       try {

subject.checkRole("role1");  //该角色不存在则抛出Un authorized Exception

       } catch (AuthorizationException e) {

// TODO Auto-generated catch block

e.printStackTrace();

           System.out.println("角色不存在");

       }

//判断有多少个角色

       boolean[] hasRoles = subject.hasRoles(Arrays.asList("role1","role2"));

//subject.checkRoles("role1","role2");

      

//基于资源的授权

       boolean userAdd = subject.isPermitted("user:add");

       System.out.println("subject:zhangsan具有userAdd的权限吗"+userAdd);

//判断具有多个授权

subject.isPermittedAll("user:add","user:delete");   

    }

}

 

[users]

zhangsan=1111,role1

lisi=1111,role2

[roles]

role1=user:add,user:update,user:delete

role2=user:*

Shiro的权限检查方式

方式1:编程式

方式2:注解式

方式3::标签式

If(subject.hasRole(“admin”))

@RequiresRoles(“admin”):在执行指定的方法时会检查是否具有该权限,不需要了url的拦截 

Public void list()

 

 

 

授权流程:

  获取subject的主体

  判断主体是否通过认证

  调用subject.isPermitted()/hasRole()的方法来进行权限的判断

Subject是由其实现类DelegatingSubject来调用方法的,该类将处理交给了SecurityManager;

SecurityManager是由其实现类DefaultSecurityManager的isPermitted的方法

来处理,本质是交给其父类-AuthorazitionSecurityManager来处理的。该类将处理处理交给了authorzer()授权器来处理的

Authrozer()由其实现类ModularRealmAuthorzer来处理。该类可以调用调用对应的Realm来获取数据,在该类有PermissionResolver对权限字符串进行解析,在对应的Realm中也有对应的PermissionResolver交给WildcardPermissionResolver该类调用WildcardPermission来进行权限字符串的解析。然后返回处理结果。

 

自定义Realm实现授权

通过配置文件指定权限不够灵活,并且不方便。在实际应用中大多数情况下都是将用户信息,角色信息保存到数据库中。所以需要从数据库中获取相关的数据信息。可以使用Shiro提供的jdbcRealm来实现,可以自定义Realm来实现。使用JDBCRealm往往也不够灵活,所以在实际应用中大多数情况下都是自定义Realm来实现。

自定义Realm需要继承AuthorazingRealm,重写方法

package com.pshdhx;

 

import java.util.ArrayList;

import java.util.List;

 

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.authz.AuthorizationInfo;

import org.apache.shiro.authz.SimpleAuthorizationInfo;

import org.apache.shiro.realm.AuthorizingRealm;

import org.apache.shiro.subject.PrincipalCollection;

importorg.apache.shiro.util.ByteSource;

/**

 * 自定义Realm的实现,该Realm类提供了两个方法

 * doGetAuthorizationInfo 获取认证信息

 * doGetAuthenticationInfo 获取权限信息

 */

publicclass UserRealm extends AuthorizingRealm{

   

@Override

    public String getName(){

       return "userRealm";

    }

   

//身份认证

@Override

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

// TODO Auto-generated method stub

              String username = (String)token.getPrincipal();  //获取身份信息

              System.out.println("username===="+username);

              String pwd = "1111";//相当于从数据库中取得

//pwd="b59c67bf196a4758191e42f76670ceba";

//String salt = "pshdhx";

//将数据库中查询到的信息封装到SimpleAuthenticationInfo中

              SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,pwd,getName());

              return info;

    }

   

//授权的信息

@Override

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {

// TODO Auto-generated method stub

       String username  = (String)principal.getPrimaryPrincipal();

//根据用户名去数据库中查询该用户对应的权限的

       List<String> permission  = new ArrayList<>();

permission.add("user:add");

permission.add("user:delete");

permission.add("user:find");

permission.add("user:update");

       SimpleAuthorizationInfo  info = new SimpleAuthorizationInfo();

       for(String perms : permission){

info.addStringPermission(perms);

       }

       return info;

    }

   

   

   

}

 

[main]

userRealm=com.pshdhx.UserRealm

securityManager.realm=$userRealm

[users]

zhangsan=1111

lisi=1111

package com.pshdhx;

 

importjava.lang.reflect.Array;

 

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.AuthenticationException;

import org.apache.shiro.authc.UsernamePasswordToken;

import org.apache.shiro.config.IniSecurityManagerFactory;

import org.apache.shiro.mgt.SecurityManager;

import org.apache.shiro.subject.Subject;

import org.apache.shiro.util.Factory;

publicclass UserRealmDemo {

    publicstaticvoid main(String[] args) {

       Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");

       SecurityManager securityManager = factory.getInstance();

       SecurityUtils.setSecurityManager(securityManager);

       Subject subject = SecurityUtils.getSubject();

       UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "1111");

       try {

subject.login(token);

           if(subject.isAuthenticated()){

              System.out.println("验证通过");

           }

       } catch (AuthenticationException e) {

// TODO Auto-generated catch block

e.printStackTrace();

           System.out.println("验证不通过");

       }

       boolean[] permitted = subject.isPermitted("user:add","user:delete");

       System.out.println(permitted[1]+"=="+permitted[0]);

    }

}

验证通过

true==true