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 |