首页 web前端 js教程 shiro授权的实现原理实例分享

shiro授权的实现原理实例分享

Feb 07, 2018 am 11:41 AM
shiro 原理 实例

授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。本文主要和大家介绍shiro授权的实现原理,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望能帮助到大家。

主体

主体,即访问应用的用户,在Shiro中使用Subject代表该用户。用户只有授权后才允许访问相应的资源。

资源

在应用中用户可以访问的任何东西,比如访问JSP页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。

权限

安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:

访问用户列表页面

查看/新增/修改/删除用户数据(即很多时候都是CRUD(增查改删)式权限控制)

打印文档等等。。。

如上可以看出,权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许,不反映谁去执行这个操作。所以后续还需要把权限赋予给用户,即定义哪个用户允许在某个资源上做什么操作(权限),Shiro不会去做这件事情,而是由实现人员提供。

Shiro支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例级别的),后续部分介绍。

角色

角色代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。
隐式角色:即直接通过角色来验证用户有没有操作权限,如在应用中CTO、技术总监、开发工程师可以使用打印机,假设某天不允许开发工程师使用打印机,此时需要从应用中删除相应代码;再如在应用中CTO、技术总监可以查看用户、查看权限;突然有一天不允许技术总监查看用户、查看权限了,需要在相关代码中把技术总监角色从判断逻辑中删除掉;即粒度是以角色为单位进行访问控制的,粒度较粗;如果进行修改可能造成多处代码修改。

显示角色:在程序中通过权限控制谁能访问某个资源,角色聚合一组权限集合;这样假设哪个角色不能访问某个资源,只需要从角色代表的权限集合中移除即可;无须修改多处代码;即粒度是以资源/实例为单位的;粒度较细。

请google搜索“RBAC”和“RBAC新解”分别了解“基于角色的访问控制”“基于资源的访问控制(Resource-Based Access Control)”。

授权方式

Shiro支持三种方式的授权:

编程式:通过写if/else授权代码块完成: 


Subject subject = SecurityUtils.getSubject(); 
if(subject.hasRole(“admin”)) { 
  //有权限 
} else { 
  //无权限 
}
登录后复制

注解式:通过在执行的Java方法上放置相应的注解完成:


@RequiresRoles("admin") 
public void hello() { 
  //有权限 
}
登录后复制

没有权限将抛出相应的异常;

JSP/GSP标签:在JSP/GSP页面通过相应的标签完成:


<shiro:hasRole name="admin"> 
<!— 有权限 —> 
</shiro:hasRole>
登录后复制

后续部分将详细介绍如何使用。

授权

基于角色的访问控制(隐式角色)

1、在ini配置文件配置用户拥有的角色(shiro-role.ini)


[users] 
zhang=123,role1,role2 
wang=123,role1
登录后复制

规则即:“用户名=密码,角色1,角色2”,如果需要在应用中判断用户是否有相应角色,就需要在相应的Realm中返回角色信息,也就是说Shiro不负责维护用户-角色信息,需要应用提供,Shiro只是提供相应的接口方便验证,后续会介绍如何动态的获取用户角色。

2、测试用例(com.github.zhangkaitao.shiro.chapter3.RoleTest)


@Test 
public void testHasRole() { 
  login("classpath:shiro-role.ini", "zhang", "123"); 
  //判断拥有角色:role1 
  Assert.assertTrue(subject().hasRole("role1")); 
  //判断拥有角色:role1 and role2 
  Assert.assertTrue(subject().hasAllRoles(Arrays.asList("role1", "role2"))); 
  //判断拥有角色:role1 and role2 and !role3 
  boolean[] result = subject().hasRoles(Arrays.asList("role1", "role2", "role3")); 
  Assert.assertEquals(true, result[0]); 
  Assert.assertEquals(true, result[1]); 
  Assert.assertEquals(false, result[2]); 
}
登录后复制

Shiro提供了hasRole/hasRole用于判断用户是否拥有某个角色/某些权限;但是没有提供如hashAnyRole用于判断是否有某些权限中的某一个。


@Test(expected = UnauthorizedException.class) 
public void testCheckRole() { 
  login("classpath:shiro-role.ini", "zhang", "123"); 
  //断言拥有角色:role1 
  subject().checkRole("role1"); 
  //断言拥有角色:role1 and role3 失败抛出异常 
  subject().checkRoles("role1", "role3"); 
}
登录后复制

Shiro提供的checkRole/checkRoles和hasRole/hasAllRoles不同的地方是它在判断为假的情况下会抛出UnauthorizedException异常。

到此基于角色的访问控制(即隐式角色)就完成了,这种方式的缺点就是如果很多地方进行了角色判断,但是有一天不需要了那么就需要修改相应代码把所有相关的地方进行删除;这就是粗粒度造成的问题。

基于资源的访问控制(显示角色)

1、在ini配置文件配置用户拥有的角色及角色-权限关系(shiro-permission.ini)


[users] 
zhang=123,role1,role2 
wang=123,role1 
[roles] 
role1=user:create,user:update 
role2=user:create,user:delete
登录后复制

规则:“用户名=密码,角色1,角色2”“角色=权限1,权限2”,即首先根据用户名找到角色,然后根据角色再找到权限;即角色是权限集合;Shiro同样不进行权限的维护,需要我们通过Realm返回相应的权限信息。只需要维护“用户——角色”之间的关系即可。

2、测试用例(com.github.zhangkaitao.shiro.chapter3.PermissionTest)


@Test 
public void testIsPermitted() { 
  login("classpath:shiro-permission.ini", "zhang", "123"); 
  //判断拥有权限:user:create 
  Assert.assertTrue(subject().isPermitted("user:create")); 
  //判断拥有权限:user:update and user:delete 
  Assert.assertTrue(subject().isPermittedAll("user:update", "user:delete")); 
  //判断没有权限:user:view 
  Assert.assertFalse(subject().isPermitted("user:view")); 
}
登录后复制

Shiro提供了isPermitted和isPermittedAll用于判断用户是否拥有某个权限或所有权限,也没有提供如isPermittedAny用于判断拥有某一个权限的接口。


@Test(expected = UnauthorizedExceptionclass) 
public void testCheckPermission () { 
  login("classpath:shiro-permissionini", "zhang", "123"); 
  //断言拥有权限:user:create 
  subject().checkPermission("user:create"); 
  //断言拥有权限:user:delete and user:update 
  subject().checkPermissions("user:delete", "user:update"); 
  //断言拥有权限:user:view 失败抛出异常 
  subject().checkPermissions("user:view"); 
}
登录后复制

但是失败的情况下会抛出UnauthorizedException异常。

到此基于资源的访问控制(显示角色)就完成了,也可以叫基于权限的访问控制,这种方式的一般规则是“资源标识符:操作”,即是资源级别的粒度;这种方式的好处就是如果要修改基本都是一个资源级别的修改,不会对其他模块代码产生影响,粒度小。但是实现起来可能稍微复杂点,需要维护“用户——角色,角色——权限(资源:操作)”之间的关系。

Permission

字符串通配符权限

规则:“资源标识符:操作:对象实例ID” 即对哪个资源的哪个实例可以进行什么操作。其默认支持通配符权限字符串,“:”表示资源/操作/实例的分割;“,”表示操作的分割;“*”表示任意资源/操作/实例。

1、单个资源单个权限


subject().checkPermissions("system:user:update");
登录后复制

用户拥有资源“system:user”的“update”权限。

2、单个资源多个权限

ini配置文件


role41=system:user:update,system:user:delete
登录后复制

然后通过如下代码判断


subject().checkPermissions("system:user:update", "system:user:delete");
登录后复制

用户拥有资源“system:user”的“update”和“delete”权限。如上可以简写成:

ini配置(表示角色4拥有system:user资源的update和delete权限)


role42="system:user:update,delete"
登录后复制

接着可以通过如下代码判断


subject().checkPermissions("system:user:update,delete");
登录后复制

通过“system:user:update,delete”验证"system:user:update, system:user:delete"是没问题的,但是反过来是规则不成立。

3、单个资源全部权限

ini配置


role51="system:user:create,update,delete,view"
登录后复制

然后通过如下代码判断


subject().checkPermissions("system:user:create,delete,update:view");
登录后复制

用户拥有资源“system:user”的“create”、“update”、“delete”和“view”所有权限。如上可以简写成:

ini配置文件(表示角色5拥有system:user的所有权限)


role52=system:user:*
登录后复制

也可以简写为(推荐上边的写法):


role53=system:user
登录后复制

然后通过如下代码判断


subject().checkPermissions("system:user:*"); 
subject().checkPermissions("system:user");
登录后复制

通过“system:user:*”验证“system:user:create,delete,update:view”可以,但是反过来是不成立的。

4、所有资源全部权限

ini配置


role61=*:view
登录后复制

然后通过如下代码判断


subject().checkPermissions("user:view");
登录后复制

用户拥有所有资源的“view”所有权限。假设判断的权限是“"system:user:view”,那么需要“role5=*:*:view”这样写才行。

5、实例级别的权限

5.1、单个实例单个权限

ini配置


role71=user:view:1
登录后复制

对资源user的1实例拥有view权限。

然后通过如下代码判断


subject().checkPermissions("user:view:1");
登录后复制

5.2、单个实例多个权限

ini配置


role72="user:update,delete:1"
登录后复制

对资源user的1实例拥有update、delete权限。

然后通过如下代码判断


subject().checkPermissions("user:delete,update:1"); 
subject().checkPermissions("user:update:1", "user:delete:1");
登录后复制

5.3、单个实例所有权限

ini配置


role73=user:*:1
登录后复制

对资源user的1实例拥有所有权限。

然后通过如下代码判断


subject().checkPermissions("user:update:1", "user:delete:1", "user:view:1");
登录后复制

5.4、所有实例单个权限

ini配置


role74=user:auth:*
登录后复制

对资源user的1实例拥有所有权限。

然后通过如下代码判断


subject().checkPermissions("user:auth:1", "user:auth:2");
登录后复制

5.5、所有实例所有权限

ini配置


role75=user:*:*
登录后复制

对资源user的1实例拥有所有权限。

然后通过如下代码判断


subject().checkPermissions("user:view:1", "user:auth:2");
登录后复制

6、Shiro对权限字符串缺失部分的处理

如“user:view”等价于“user:view:*”;而“organization”等价于“organization:*”或者“organization:*:*”。可以这么理解,这种方式实现了前缀匹配。

另外如“user:*”可以匹配如“user:delete”、“user:delete”可以匹配如“user:delete:1”、“user:*:1”可以匹配如“user:view:1”、“user”可以匹配“user:view”或“user:view:1”等。即*可以匹配所有,不加*可以进行前缀匹配;但是如“*:view”不能匹配“system:user:view”,需要使用“*:*:view”,即后缀匹配必须指定前缀(多个冒号就需要多个*来匹配)。

7、WildcardPermission

如下两种方式是等价的:


subject().checkPermission("menu:view:1"); 
subject().checkPermission(new WildcardPermission("menu:view:1"));
登录后复制

因此没什么必要的话使用字符串更方便。

8、性能问题

通配符匹配方式比字符串相等匹配来说是更复杂的,因此需要花费更长时间,但是一般系统的权限不会太多,且可以配合缓存来提供其性能,如果这样性能还达不到要求我们可以实现位操作算法实现性能更好的权限匹配。另外实例级别的权限验证如果数据量太大也不建议使用,可能造成查询权限及匹配变慢。可以考虑比如在sql查询时加上权限字符串之类的方式在查询时就完成了权限匹配。

授权流程

流程如下:

1、首先调用Subject.isPermitted*/hasRole*接口,其会委托给SecurityManager,而SecurityManager接着会委托给Authorizer;
2、Authorizer是真正的授权者,如果我们调用如isPermitted(“user:view”),其首先会通过PermissionResolver把字符串转换成相应的Permission实例;
3、在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限;
4、Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted*/hasRole*会返回true,否则返回false表示授权失败。

ModularRealmAuthorizer进行多Realm匹配流程:

1、首先检查相应的Realm是否实现了实现了Authorizer;
2、如果实现了Authorizer,那么接着调用其相应的isPermitted*/hasRole*接口进行匹配;
3、如果有一个Realm匹配那么将返回true,否则返回false。

如果Realm进行授权的话,应该继承AuthorizingRealm,其流程是:

1.1、如果调用hasRole*,则直接获取AuthorizationInfo.getRoles()与传入的角色比较即可;
1.2、首先如果调用如isPermitted(“user:view”),首先通过PermissionResolver将权限字符串转换成相应的Permission实例,默认使用WildcardPermissionResolver,即转换为通配符的WildcardPermission;

2、通过AuthorizationInfo.getObjectPermissions()得到Permission实例集合;通过AuthorizationInfo. getStringPermissions()得到字符串集合并通过PermissionResolver解析为Permission实例;然后获取用户的角色,并通过RolePermissionResolver解析角色对应的权限集合(默认没有实现,可以自己提供);

3、接着调用Permission. implies(Permission p)逐个与传入的权限比较,如果有匹配的则返回true,否则false。

Authorizer、PermissionResolver及RolePermissionResolver

Authorizer的职责是进行授权(访问控制),是Shiro API中授权核心的入口点,其提供了相应的角色/权限判断接口,具体请参考其Javadoc。SecurityManager继承了Authorizer接口,且提供了ModularRealmAuthorizer用于多Realm时的授权匹配。PermissionResolver用于解析权限字符串到Permission实例,而RolePermissionResolver用于根据角色解析相应的权限集合。

我们可以通过如下ini配置更改Authorizer实现:


authorizer=org.apache.shiro.authz.ModularRealmAuthorizer 
securityManager.authorizer=$authorizer
登录后复制

对于ModularRealmAuthorizer,相应的AuthorizingSecurityManager会在初始化完成后自动将相应的realm设置进去,我们也可以通过调用其setRealms()方法进行设置。对于实现自己的authorizer可以参考ModularRealmAuthorizer实现即可,在此就不提供示例了。

设置ModularRealmAuthorizer的permissionResolver,其会自动设置到相应的Realm上(其实现了PermissionResolverAware接口),如:


permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolver 
authorizer.permissionResolver=$permissionResolver
登录后复制

设置ModularRealmAuthorizer的rolePermissionResolver,其会自动设置到相应的Realm上(其实现了RolePermissionResolverAware接口),如:


rolePermissionResolver=com.github.zhangkaitao.shiro.chapter3.permission.MyRolePermissionResolver 
authorizer.rolePermissionResolver=$rolePermissionResolver
登录后复制

示例

1、ini配置(shiro-authorizer.ini)


[main] 
#自定义authorizer 
authorizer=org.apache.shiro.authz.ModularRealmAuthorizer 
#自定义permissionResolver 
#permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolver 
permissionResolver=com.github.zhangkaitao.shiro.chapter3.permission.BitAndWildPermissionResolver 
authorizer.permissionResolver=$permissionResolver 
#自定义rolePermissionResolver 
rolePermissionResolver=com.github.zhangkaitao.shiro.chapter3.permission.MyRolePermissionResolver 
1authorizer.rolePermissionResolver=$rolePermissionResolver 
 
securityManager.authorizer=$authorizer
登录后复制


#自定义realm 一定要放在securityManager.authorizer赋值之后(因为调用setRealms会将realms设置给authorizer,并给各个Realm设置permissionResolver和rolePermissionResolver) 
realm=com.github.zhangkaitao.shiro.chapter3.realm.MyRealm 
securityManager.realms=$realm
登录后复制

设置securityManager 的realms一定要放到最后,因为在调用SecurityManager.setRealms时会将realms设置给authorizer,并为各个Realm设置permissionResolver和rolePermissionResolver。另外,不能使用IniSecurityManagerFactory创建的IniRealm,因为其初始化顺序的问题可能造成后续的初始化Permission造成影响。

2、定义BitAndWildPermissionResolver及BitPermission

BitPermission用于实现位移方式的权限,如规则是:

权限字符串格式:+资源字符串+权限位+实例ID;以+开头中间通过+分割;权限:0 表示所有权限;1 新增(二进制:0001)、2 修改(二进制:0010)、4 删除(二进制:0100)、8 查看(二进制:1000);如 +user+10 表示对资源user拥有修改/查看权限。


public class BitPermission implements Permission { 
  private String resourceIdentify; 
  private int permissionBit; 
  private String instanceId; 
  public BitPermission(String permissionString) { 
    String[] array = permissionString.split("\\+"); 
    if(array.length > 1) { 
      resourceIdentify = array[1]; 
    } 
    if(StringUtils.isEmpty(resourceIdentify)) { 
      resourceIdentify = "*"; 
    } 
    if(array.length > 2) { 
      permissionBit = Integer.valueOf(array[2]); 
    } 
    if(array.length > 3) { 
      instanceId = array[3]; 
    } 
    if(StringUtils.isEmpty(instanceId)) { 
      instanceId = "*"; 
    } 
  } 
 
  @Override 
  public boolean implies(Permission p) { 
    if(!(p instanceof BitPermission)) { 
      return false; 
    } 
    BitPermission other = (BitPermission) p; 
    if(!("*".equals(this.resourceIdentify) || this.resourceIdentify.equals(other.resourceIdentify))) { 
      return false; 
    } 
    if(!(this.permissionBit ==0 || (this.permissionBit & other.permissionBit) != 0)) { 
      return false; 
    } 
    if(!("*".equals(this.instanceId) || this.instanceId.equals(other.instanceId))) { 
      return false; 
    } 
    return true; 
  } 
}  
Permission接口提供了boolean implies(Permission p)方法用于判断权限匹配的;
public class BitAndWildPermissionResolver implements PermissionResolver { 
  @Override 
  public Permission resolvePermission(String permissionString) { 
    if(permissionString.startsWith("+")) { 
      return new BitPermission(permissionString); 
    } 
    return new WildcardPermission(permissionString); 
  } 
}
登录后复制

BitAndWildPermissionResolver实现了PermissionResolver接口,并根据权限字符串是否以“+”开头来解析权限字符串为BitPermission或WildcardPermission。

3、定义MyRolePermissionResolver

RolePermissionResolver用于根据角色字符串来解析得到权限集合。


public class MyRolePermissionResolver implements RolePermissionResolver { 
  @Override 
  public Collection<Permission> resolvePermissionsInRole(String roleString) { 
    if("role1".equals(roleString)) { 
      return Arrays.asList((Permission)new WildcardPermission("menu:*")); 
    } 
    return null; 
  } 
}
登录后复制

此处的实现很简单,如果用户拥有role1,那么就返回一个“menu:*”的权限。

4、自定义Realm


public class MyRealm extends AuthorizingRealm { 
  @Override 
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); 
    authorizationInfo.addRole("role1"); 
    authorizationInfo.addRole("role2"); 
    authorizationInfo.addObjectPermission(new BitPermission("+user1+10")); 
    authorizationInfo.addObjectPermission(new WildcardPermission("user1:*")); 
    authorizationInfo.addStringPermission("+user2+10"); 
    authorizationInfo.addStringPermission("user2:*"); 
    return authorizationInfo; 
  } 
  @Override 
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 
    //和com.github.zhangkaitao.shiro.chapter2.realm.MyRealm1. getAuthenticationInfo代码一样,省略 
} 
}
登录后复制

此时我们继承AuthorizingRealm而不是实现Realm接口;推荐使用AuthorizingRealm,因为:
AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token):表示获取身份验证信息;
AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals):表示根据用户身份获取授权信息。
这种方式的好处是当只需要身份验证时只需要获取身份验证信息而不需要获取授权信息。对于AuthenticationInfo和AuthorizationInfo请参考其Javadoc获取相关接口信息。

另外我们可以使用JdbcRealm,需要做的操作如下:

1、执行sql/ shiro-init-data.sql 插入相关的权限数据;
2、使用shiro-jdbc-authorizer.ini配置文件,需要设置jdbcRealm.permissionsLookupEnabled为true来开启权限查询。

此次还要注意就是不能把我们自定义的如“+user1+10”配置到INI配置文件,即使有IniRealm完成,因为IniRealm在new完成后就会解析这些权限字符串,默认使用了WildcardPermissionResolver完成,即此处是一个设计权限,如果采用生命周期(如使用初始化方法)的方式进行加载就可以解决我们自定义permissionResolver的问题。

5、测试用例


public class AuthorizerTest extends BaseTest { 
 
  @Test 
  public void testIsPermitted() { 
    login("classpath:shiro-authorizer.ini", "zhang", "123"); 
    //判断拥有权限:user:create 
    Assert.assertTrue(subject().isPermitted("user1:update")); 
    Assert.assertTrue(subject().isPermitted("user2:update")); 
    //通过二进制位的方式表示权限 
    Assert.assertTrue(subject().isPermitted("+user1+2"));//新增权限 
    Assert.assertTrue(subject().isPermitted("+user1+8"));//查看权限 
    Assert.assertTrue(subject().isPermitted("+user2+10"));//新增及查看 
 
    Assert.assertFalse(subject().isPermitted("+user1+4"));//没有删除权限 
 
    Assert.assertTrue(subject().isPermitted("menu:view"));//通过MyRolePermissionResolver解析得到的权限 
  } 
}
登录后复制

通过如上步骤可以实现自定义权限验证了。

相关推荐:

shiro源码的详细介绍

实例讲解shiro登录认证和权限控制

关于shiro的源码学习之Session session = getSession()的实例分析

以上是shiro授权的实现原理实例分享的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
威尔R.E.P.O.有交叉游戏吗?
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

nohup的作用及原理解析 nohup的作用及原理解析 Mar 25, 2024 pm 03:24 PM

nohup的作用及原理解析在Unix和类Unix操作系统中,nohup是一个常用的命令,用于在后台运行命令,即便用户退出当前会话或关闭终端窗口,命令仍然能够继续执行。在本文中,我们将详细解析nohup命令的作用和原理。一、nohup的作用后台运行命令:通过nohup命令,我们可以让需要长时间运行的命令在后台持续执行,而不受用户退出终端会话的影响。这在需要运行

深度探讨Struts框架的原理与实践 深度探讨Struts框架的原理与实践 Feb 18, 2024 pm 06:10 PM

Struts框架的原理解析与实践探索Struts框架作为JavaWeb开发中常用的MVC框架,具有良好的设计模式和可扩展性,广泛应用于企业级应用程序开发中。本文将对Struts框架的原理进行解析,并结合实际代码示例进行探索,帮助读者更好地理解和应用该框架。一、Struts框架的原理解析1.MVC架构Struts框架基于MVC(Model-View-Con

深入理解MyBatis中的批量Insert实现原理 深入理解MyBatis中的批量Insert实现原理 Feb 21, 2024 pm 04:42 PM

MyBatis是一款流行的Java持久层框架,广泛应用于各种Java项目中。其中,批量插入是一个常见的操作,可以有效提升数据库操作的性能。本文将深入探讨MyBatis中的批量Insert实现原理,并结合具体的代码示例进行详细解析。MyBatis中的批量Insert在MyBatis中,批量Insert操作通常使用动态SQL来实现。通过构建一条包含多个插入值的S

深入探讨Linux RPM工具的作用和原理 深入探讨Linux RPM工具的作用和原理 Feb 23, 2024 pm 03:00 PM

Linux系统中的RPM(RedHatPackageManager)工具是一种用于安装、升级、卸载和管理系统软件包的强大工具。它是RedHatLinux系统中常用的软件包管理工具,也被许多其他Linux发行版采用。RPM工具的作用非常重要,它使得系统管理员和用户能够方便地管理系统上的软件包。通过RPM,用户可以很容易地安装新的软件包,升级现有的软件

MyBatis分页插件原理详解 MyBatis分页插件原理详解 Feb 22, 2024 pm 03:42 PM

MyBatis是一个优秀的持久层框架,它支持基于XML和注解的方式操作数据库,简单易用,同时也提供了丰富的插件机制。其中,分页插件是使用频率较高的插件之一。本文将深入探讨MyBatis分页插件的原理,并结合具体的代码示例进行说明。一、分页插件原理MyBatis本身并不提供原生的分页功能,但可以借助插件来实现分页查询。分页插件的原理主要是通过拦截MyBatis

深度解析Linux chage命令的功能与工作原理 深度解析Linux chage命令的功能与工作原理 Feb 24, 2024 pm 03:48 PM

Linux系统中的chage命令是用来修改用户账号的密码失效日期的命令,也可以用来修改账号的最长和最短可用日期等。该命令在管理用户账号安全上起到非常重要的作用,可以有效地控制用户密码的使用期限,增强系统的安全性。chage命令的使用方法:chage命令的基本语法为:chage[选项]用户名例如,要修改用户“testuser”的密码失效日期,可以使用以下命

Golang实现继承方法的基本原理和方式 Golang实现继承方法的基本原理和方式 Jan 20, 2024 am 09:11 AM

Golang继承方法的基本原理与实现方式在Golang中,继承是面向对象编程的重要特性之一。通过继承,我们可以使用父类的属性和方法,从而实现代码的复用和扩展性。本文将介绍Golang继承方法的基本原理和实现方式,并提供具体的代码示例。继承方法的基本原理在Golang中,继承是通过嵌入结构体的方式实现的。当一个结构体嵌入另一个结构体时,被嵌入的结构体就拥有了嵌

Astar质押原理、收益拆解、空投项目及策略 & 操作保姆级攻略 Astar质押原理、收益拆解、空投项目及策略 & 操作保姆级攻略 Jun 25, 2024 pm 07:09 PM

目录Astar Dapp 质押原理质押收益 拆解潜在空投项目:AlgemNeurolancheHealthreeAstar Degens DAOVeryLongSwap 质押策略 & 操作“AstarDapp质押”今年初已升级至V3版本,对质押收益规则做了不少调整。目前首个质押周期已结束,第二质押周期的“投票”子周期刚开始。要获取“额外奖励”收益,需把握此关键阶段(预计持续至6月26日,现余不到5天)。我将细致拆解Astar质押收益,

See all articles