• [技术干货] shiro的active Directory认证
    Apache Shiro 提供了对 Active Directory (AD) 的直接支持,通过 ActiveDirectoryRealm 可以轻松集成企业级 AD 环境,实现集中式身份认证。以下是 Shiro 中实现 AD 认证的完整指南,包括配置、代码示例和常见问题解决方案。1. Active Directory 认证基础作用:通过 AD 服务器验证用户凭据(用户名/密码),并获取用户角色和权限。优势:直接利用企业现有的 AD 基础设施,无需单独维护用户数据库。支持组权限管理(通过 AD 的组策略)。适用于 Windows 域环境下的单点登录(SSO)。2. Shiro AD 认证实现步骤(1) 添加依赖确保项目中包含 Shiro 和 AD 相关依赖(Maven 示例):<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.12.0</version> <!-- 使用最新版本 --> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-activedirectory</artifactId> <version>1.12.0</version> </dependency> (2) 配置 shiro.ini 文件通过 shiro.ini 配置 AD 认证的核心参数:[main] # 配置 ActiveDirectoryRealm activeDirectoryRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm activeDirectoryRealm.url = ldap://ad.example.com:389 # AD 服务器地址 activeDirectoryRealm.systemUsername = CN=admin,CN=Users,DC=example,DC=com # 系统管理员 DN activeDirectoryRealm.systemPassword = admin123 # 系统管理员密码 activeDirectoryRealm.searchBase = CN=Users,DC=example,DC=com # 用户搜索基路径 activeDirectoryRealm.groupRolesMap = "CN=Developers,CN=Users,DC=example,DC=com:developer,CN=Admins,CN=Users,DC=example,DC=com:admin" # AD 组到 Shiro 角色的映射 # 可选:启用调试日志 loggerFactory.logLevel = debug关键参数说明:url:AD 服务器地址(如 ldap://ad.example.com:389)。systemUsername 和 systemPassword:用于搜索用户的系统管理员凭据(需具有读取权限)。searchBase:用户搜索的基路径(通常是 CN=Users,DC=example,DC=com)。groupRolesMap:AD 组到 Shiro 角色的映射(格式:AD组DN:Shiro角色)。(3) 编写 Java 代码进行认证import org.apache.shiro.SecurityUtils; 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; public class AdAuthDemo { public static void main(String[] args) { // 初始化 SecurityManager Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); // 获取当前用户 Subject currentUser = SecurityUtils.getSubject(); // 创建用户名/密码 Token UsernamePasswordToken token = new UsernamePasswordToken("user1", "password123"); try { // 执行认证 currentUser.login(token); System.out.println("AD 认证成功!"); System.out.println("用户角色: " + currentUser.getPrincipals().asList()); // 输出角色 } catch (Exception e) { System.out.println("AD 认证失败:" + e.getMessage()); } } } 3. 高级配置(1) 启用 SSL/TLS如果 AD 服务器使用安全连接:[main] activeDirectoryRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm activeDirectoryRealm.url = ldaps://ad.example.com:636 # 使用 ldaps activeDirectoryRealm.systemUsername = CN=admin,CN=Users,DC=example,DC=com activeDirectoryRealm.systemPassword = admin123(2) 自定义搜索过滤器如果默认搜索不满足需求,可以自定义搜索过滤器:[main] activeDirectoryRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm activeDirectoryRealm.url = ldap://ad.example.com:389 activeDirectoryRealm.searchBase = CN=Users,DC=example,DC=com activeDirectoryRealm.searchFilter = (sAMAccountName={0}) # 使用 AD 的 sAMAccountName 属性(3) 动态角色映射如果需要动态映射 AD 组到 Shiro 角色,可以继承 ActiveDirectoryRealm 并重写方法:import org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm; import org.apache.shiro.subject.PrincipalCollection; import java.util.HashMap; import java.util.Map; public class CustomAdRealm extends ActiveDirectoryRealm { @Override protected Map<String, String> getGroupRolesMap(PrincipalCollection principals) { Map<String, String> rolesMap = new HashMap<>(); // 动态映射逻辑(例如从数据库加载) rolesMap.put("CN=Developers,CN=Users,DC=example,DC=com", "developer"); rolesMap.put("CN=Admins,CN=Users,DC=example,DC=com", "admin"); return rolesMap; } } 然后在 shiro.ini 中配置:[main] customAdRealm = com.example.CustomAdRealm customAdRealm.url = ldap://ad.example.com:389 securityManager.realms = $customAdRealm4. 常见问题与解决方案(1) 认证失败:Invalid credentials原因:用户名或密码错误,或 AD 服务器配置不正确。解决:检查 systemUsername 和 systemPassword 是否正确。确认 AD 用户的 sAMAccountName 是否与输入的用户名匹配。(2) 认证失败:Connection refused原因:AD 服务器地址或端口错误。解决:确认 url 是否正确(如 ldap://ad.example.com:389)。检查防火墙是否放行 LDAP 端口(389 或 636)。(3) 角色映射不生效原因:groupRolesMap 配置错误或 AD 组路径不正确。解决:使用 AD 工具(如 Active Directory Users and Computers)确认组 DN。确保 groupRolesMap 的格式为 AD组DN:Shiro角色。(4) 性能问题原因:频繁的 AD 查询导致延迟。解决:使用缓存(如 Shiro 的 CachingRealm)。优化搜索过滤器,减少返回的数据量。5. 总结Shiro 的 Active Directory 认证通过 ActiveDirectoryRealm 实现,核心步骤包括:配置 shiro.ini 文件,设置 AD 服务器地址、系统管理员凭据、搜索基路径和角色映射。编写 Java 代码,通过 Subject.login() 执行认证。根据需求调整高级配置(如 SSL、自定义搜索、动态角色映射)。适用场景:企业级应用需要与现有 AD 环境集成。需要基于 AD 组管理用户角色和权限。适用于 Windows 域环境下的单点登录(SSO)。通过合理配置,Shiro 可以高效、安全地实现与 Active Directory 的认证集成。
  • [技术干货] Shiro 的 LDAP 认证
    Shiro 的 LDAP 认证方式详解Apache Shiro 提供了对 LDAP(轻量级目录访问协议)的集成支持,用于企业级系统的集中式身份认证。以下是 Shiro 中实现 LDAP 认证的完整指南,包括配置、代码示例和关键点说明。1. LDAP 认证基础作用:通过 LDAP 服务器(如 Active Directory、OpenLDAP)验证用户凭据(用户名/密码)。优势:集中式用户管理,无需在应用中维护密码。支持企业级目录服务,便于集成现有系统。2. Shiro LDAP 认证实现步骤(1) 添加依赖确保项目中包含 Shiro 和 LDAP 相关依赖(以 Maven 为例):<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.12.0</version> <!-- 使用最新版本 --> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ldap</artifactId> <version>1.12.0</version> </dependency> (2) 配置 shiro.ini 文件通过 shiro.ini 配置 LDAP 认证的核心参数:[main] # 配置 LDAP Realm ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm ldapRealm.contextFactory.url = ldap://ldap.example.com:389 # LDAP 服务器地址 ldapRealm.contextFactory.authenticationMechanism = simple # 认证机制(simple/DIGEST-MD5) ldapRealm.userDnTemplate = uid={0},ou=users,dc=example,dc=com # 用户 DN 模板 ldapRealm.searchBase = dc=example,dc=com # 搜索基路径 ldapRealm.systemUsername = cn=admin,dc=example,dc=com # 系统管理员 DN(用于搜索) ldapRealm.systemPassword = admin123 # 系统管理员密码 # 可选:启用调试日志 loggerFactory.logLevel = debug关键参数说明:contextFactory.url:LDAP 服务器地址(如 ldap://ldap.example.com:389)。userDnTemplate:用户 DN 模板,{0} 会被替换为用户名。searchBase:搜索用户的基路径(如 dc=example,dc=com)。systemUsername 和 systemPassword:用于搜索用户的系统管理员凭据(可选,取决于 LDAP 配置)。(3) 编写 Java 代码进行认证import org.apache.shiro.SecurityUtils; 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; public class LdapAuthDemo { public static void main(String[] args) { // 初始化 SecurityManager Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); // 获取当前用户 Subject currentUser = SecurityUtils.getSubject(); // 创建用户名/密码 Token UsernamePasswordToken token = new UsernamePasswordToken("user1", "password123"); try { // 执行认证 currentUser.login(token); System.out.println("LDAP 认证成功!"); } catch (Exception e) { System.out.println("LDAP 认证失败:" + e.getMessage()); } } } 3. 高级配置(1) 自定义 LDAP 搜索如果用户 DN 不能通过模板直接生成,可以自定义搜索逻辑:[main] ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm ldapRealm.contextFactory.url = ldap://ldap.example.com:389 ldapRealm.searchBase = dc=example,dc=com ldapRealm.systemUsername = cn=admin,dc=example,dc=com ldapRealm.systemPassword = admin123 # 自定义搜索过滤器 ldapRealm.searchFilter = (uid={0}) # 搜索过滤器(默认是 (uid={0}))(2) 启用 SSL/TLS如果 LDAP 服务器使用安全连接:[main] ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm ldapRealm.contextFactory.url = ldaps://ldap.example.com:636 # 使用 ldaps ldapRealm.contextFactory.environment[java.naming.security.protocol] = ssl # 启用 SSL (3) 结合 Active DirectoryActive Directory 的配置略有不同:[main] activeDirectoryRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm activeDirectoryRealm.url = ldap://ad.example.com:389 activeDirectoryRealm.systemUsername = CN=admin,CN=Users,DC=example,DC=com activeDirectoryRealm.systemPassword = admin123 activeDirectoryRealm.searchBase = CN=Users,DC=example,DC=com4. 常见问题与解决方案(1) 认证失败:Invalid credentials原因:用户名或密码错误,或 LDAP 服务器配置不正确。解决:检查 userDnTemplate 或 searchFilter 是否正确。使用工具(如 Apache Directory Studio)测试 LDAP 连接。(2) 认证失败:Connection refused原因:LDAP 服务器地址或端口错误。解决:确认 contextFactory.url 是否正确。检查防火墙是否放行 LDAP 端口(389 或 636)。(3) 性能问题原因:频繁的 LDAP 查询导致延迟。解决:使用缓存(如 Shiro 的 CachingRealm)。优化搜索过滤器,减少返回的数据量。5. 总结Shiro 的 LDAP 认证通过 JndiLdapRealm 或 ActiveDirectoryRealm 实现,核心步骤包括:配置 shiro.ini 文件,设置 LDAP 服务器地址、用户 DN 模板等。编写 Java 代码,通过 Subject.login() 执行认证。根据需求调整高级配置(如 SSL、自定义搜索)。适用场景:企业级应用需要与现有 LDAP/AD 集成。需要集中式用户管理和单点登录(SSO)。通过合理配置,Shiro 可以轻松实现安全、可靠的 LDAP 认证。
  • [技术干货] shiro细粒度资源访问控制
    细粒度资源访问控制是 Shiro 的核心功能之一,允许开发者基于资源实例(如数据库记录、文件路径等)和操作(如读取、编辑、删除)定义权限。以下是完整的实现步骤和示例代码:1. 核心概念权限字符串格式:资源:操作(如 user:edit:123 表示编辑 ID 为 123 的用户)授权流程:通过 Subject.isPermitted("权限字符串") 动态判断权限。2. 完整实现步骤(1) 自定义 Realm 实现权限校验import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import java.util.HashSet; import java.util.Set; public class PermissionRealm extends AuthorizingRealm { // 模拟数据库存储的用户角色和权限 private static final Map<String, Set<String>> USER_ROLES = new HashMap<>(); private static final Map<String, Set<String>> ROLE_PERMISSIONS = new HashMap<>(); static { // 初始化角色和权限数据 Set<String> adminPermissions = new HashSet<>(); adminPermissions.add("user:create"); adminPermissions.add("user:edit:123"); // 允许编辑ID为123的用户 adminPermissions.add("user:delete"); ROLE_PERMISSIONS.put("admin", adminPermissions); Set<String> userPermissions = new HashSet<>(); userPermissions.add("user:view"); ROLE_PERMISSIONS.put("user", userPermissions); USER_ROLES.put("admin", new HashSet<>(Collections.singletonList("admin"))); USER_ROLES.put("user1", new HashSet<>(Arrays.asList("user", "guest"))); } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 模拟从数据库获取用户信息 UsernamePasswordToken upToken = (UsernamePasswordToken) token; String username = upToken.getUsername(); if (!"admin".equals(username) && !"user1".equals(username)) { throw new UnknownAccountException("用户不存在"); } return new SimpleAuthenticationInfo(username, "password", getName()); } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = (String) principals.getPrimaryPrincipal(); Set<String> roles = USER_ROLES.get(username); if (roles == null) { return null; } SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addRoles(roles); // 根据角色添加权限 for (String role : roles) { Set<String> permissions = ROLE_PERMISSIONS.get(role); if (permissions != null) { info.addStringPermissions(permissions); } } return info; } } (2) 配置 Shiro 使用自定义 Realmshiro.ini 配置文件:[main] permissionRealm = com.example.PermissionRealm securityManager.realms = $permissionRealm(3) 测试细粒度权限校验import org.apache.shiro.SecurityUtils; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; public class PermissionDemo { public static void main(String[] args) { // 初始化 SecurityManager Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); // 获取当前用户 Subject currentUser = SecurityUtils.getSubject(); // 模拟用户登录 currentUser.login(new org.apache.shiro.authc.UsernamePasswordToken("admin", "password")); // 测试细粒度权限 System.out.println("是否有创建用户权限: " + currentUser.isPermitted("user:create")); // true System.out.println("是否有编辑ID为123的用户权限: " + currentUser.isPermitted("user:edit:123")); // true System.out.println("是否有删除用户权限: " + currentUser.isPermitted("user:delete")); // true System.out.println("是否有编辑ID为456的用户权限: " + currentUser.isPermitted("user:edit:456")); // false(未授权) // 模拟普通用户登录 currentUser.logout(); currentUser.login(new org.apache.shiro.authc.UsernamePasswordToken("user1", "password")); System.out.println("普通用户是否有编辑权限: " + currentUser.isPermitted("user:edit:123")); // false } } 3. 关键点说明权限字符串设计:user:create:全局用户创建权限。user:edit:123:仅允许编辑 ID 为 123 的用户,实现实例级控制。动态权限校验:通过 Subject.isPermitted("权限字符串") 动态判断,无需硬编码权限逻辑。扩展性:可从数据库加载权限数据,支持动态权限变更。4. 输出结果运行 PermissionDemo 的输出:是否有创建用户权限: true 是否有编辑ID为123的用户权限: true 是否有删除用户权限: true 是否有编辑ID为456的用户权限: false 普通用户是否有编辑权限: false 总结通过自定义 Realm 和权限字符串设计,Shiro 可以轻松实现细粒度资源访问控制。关键点包括:权限字符串格式化:用 资源:操作:实例 定义权限。动态校验:通过 Subject.isPermitted() 实时判断权限。数据驱动:权限数据可从数据库动态加载,适应复杂业务场景。这种设计既灵活又安全,适用于企业级应用中的权限管理需求。
  • [技术干货] shiro的OGNL 授权策略
    Shiro 的 OGNL (Object-Graph Navigation Language) 授权策略是一种高级的权限控制方式,允许开发者通过表达式语言定义复杂的权限规则。OGNL 表达式可以访问 Java 对象的方法和属性,从而动态判断权限。1. OGNL 授权策略概述作用:通过 OGNL 表达式定义权限条件,例如 user.age > 18 或 resource.owner == currentUser。使用场景:需要基于对象属性或业务逻辑动态判断权限时。2. 实现步骤(1) 自定义 Realm 支持 OGNL 表达式在 doGetAuthorizationInfo 方法中返回 OGNL 表达式对应的权限信息。import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import java.util.Collections; import java.util.HashSet; import java.util.Set; public class OgnlPermissionRealm extends AuthorizingRealm { @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 模拟从数据库获取用户信息 UsernamePasswordToken upToken = (UsernamePasswordToken) token; String username = upToken.getUsername(); if (!"admin".equals(username)) { throw new UnknownAccountException("用户不存在"); } return new SimpleAuthenticationInfo(username, "password", getName()); } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 添加一个 OGNL 表达式权限 info.addObjectPermission(new WildcardPermission("user:edit:[id]")); return info; } } (2) 配置 Shiro 使用自定义 Realmshiro.ini 配置文件:[main] ognlPermissionRealm = com.example.OgnlPermissionRealm securityManager.realms = $ognlPermissionRealm(3) 自定义 PermissionResolver 解析 OGNL 表达式实现 PermissionResolver 接口,解析 OGNL 表达式。import org.apache.shiro.authz.Permission; import org.apache.shiro.authz.permission.PermissionResolver; import org.apache.shiro.authz.permission.WildcardPermission; public class OgnlPermissionResolver implements PermissionResolver { @Override public Permission resolvePermission(String permissionString) { // 解析 OGNL 表达式 if (permissionString.startsWith("user:edit:")) { return new OgnlPermission(permissionString); } return new WildcardPermission(permissionString); } } (4) 自定义 Permission 类import org.apache.shiro.authz.Permission; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.StringUtils; public class OgnlPermission implements Permission { private final String permissionString; public OgnlPermission(String permissionString) { this.permissionString = permissionString; } @Override public boolean implies(Subject subject, Permission permission) { // 实现 OGNL 表达式逻辑 if (permission instanceof OgnlPermission) { String otherPermission = ((OgnlPermission) permission).getPermissionString(); // 示例:解析 ID 并比较 if (otherPermission.startsWith("user:edit:")) { String id = StringUtils.substringAfter(otherPermission, "user:edit:"); // 假设从上下文中获取当前用户和资源 ID // 这里简化逻辑,直接返回 true(实际需根据业务实现) return true; } } return false; } public String getPermissionString() { return permissionString; } } (5) 测试 OGNL 权限import org.apache.shiro.SecurityUtils; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; public class OgnlPermissionDemo { public static void main(String[] args) { // 初始化 SecurityManager Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); // 获取当前用户 Subject currentUser = SecurityUtils.getSubject(); // 模拟用户登录 currentUser.login(new UsernamePasswordToken("admin", "password")); // 测试 OGNL 权限 boolean hasPermission = currentUser.isPermitted("user:edit:123"); System.out.println("是否有编辑ID为123的用户权限: " + hasPermission); // true } } 3. 关键点说明OGNL 表达式解析:通过 PermissionResolver 解析自定义的 OGNL 表达式。在 implies 方法中实现具体的业务逻辑。动态权限判断:权限字符串可以包含变量(如 [id]),在运行时动态解析。扩展性:可以结合上下文信息(如当前用户、资源实例)实现复杂权限逻辑。4. 总结Shiro 的 OGNL 授权策略提供了一种灵活的方式来实现细粒度的权限控制,特别适合需要动态解析权限表达式的场景。通过自定义 PermissionResolver 和 Permission 类,可以轻松扩展 Shiro 的权限系统以满足业务需求。
  • [技术干货] shiro认证方式介绍
    Apache Shiro 支持多种认证方式,包括基于用户名/密码、自定义 Realm、第三方系统(如 LDAP、Active Directory)等。以下是几种常见的认证方式及其使用示例:1. 基于用户名/密码的认证这是最常见的认证方式,通常通过 UsernamePasswordToken 实现。示例代码:import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; public class UsernamePasswordAuthDemo { public static void main(String[] args) { // 获取当前用户 Subject currentUser = SecurityUtils.getSubject(); // 创建用户名/密码 Token UsernamePasswordToken token = new UsernamePasswordToken("username", "password"); try { // 执行认证 currentUser.login(token); System.out.println("认证成功!"); } catch (Exception e) { System.out.println("认证失败:" + e.getMessage()); } } } 2. 自定义 Realm 认证如果需要自定义认证逻辑(例如从数据库获取用户信息),可以实现 Realm 接口。自定义 Realm 示例:import org.apache.shiro.authc.*; import org.apache.shiro.realm.Realm; public class CustomRealm implements Realm { @Override public String getName() { return "CustomRealm"; } @Override public boolean supports(AuthenticationToken token) { return token instanceof UsernamePasswordToken; } @Override public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken upToken = (UsernamePasswordToken) token; String username = upToken.getUsername(); String password = new String(upToken.getPassword()); // 模拟从数据库获取用户信息 if (!"admin".equals(username) || !"123456".equals(password)) { throw new UnknownAccountException("用户名或密码错误"); } // 返回认证信息(用户名、密码、盐、Realm 名称) return new SimpleAuthenticationInfo(username, password, getName()); } } 配置 Shiro 使用自定义 Realm:import org.apache.shiro.SecurityUtils; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; public class CustomRealmDemo { public static void main(String[] args) { // 配置 SecurityManager Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); // 获取当前用户 Subject currentUser = SecurityUtils.getSubject(); // 创建用户名/密码 Token UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456"); try { // 执行认证 currentUser.login(token); System.out.println("认证成功!"); } catch (Exception e) { System.out.println("认证失败:" + e.getMessage()); } } } shiro.ini 配置文件:[main] customRealm = com.example.CustomRealm securityManager.realms = $customRealm3. 集成 LDAP 认证Shiro 支持与 LDAP 集成,用于企业级系统的认证。示例代码:import org.apache.shiro.SecurityUtils; 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; public class LdapAuthDemo { public static void main(String[] args) { // 配置 SecurityManager Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-ldap.ini"); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); // 获取当前用户 Subject currentUser = SecurityUtils.getSubject(); // 创建用户名/密码 Token UsernamePasswordToken token = new UsernamePasswordToken("username", "password"); try { // 执行认证 currentUser.login(token); System.out.println("认证成功!"); } catch (Exception e) { System.out.println("认证失败:" + e.getMessage()); } } } shiro-ldap.ini 配置文件:[main] ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm ldapRealm.url = ldap://localhost:389 ldapRealm.contextFactory.url = ldap://localhost:389 ldapRealm.userDnTemplate = uid={0},ou=users,dc=example,dc=com4. 记住我(RememberMe)功能Shiro 支持“记住我”功能,允许用户在关闭浏览器后重新登录时无需再次输入凭据。示例代码:import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; public class RememberMeDemo { public static void main(String[] args) { // 获取当前用户 Subject currentUser = SecurityUtils.getSubject(); // 创建用户名/密码 Token,并启用“记住我”功能 UsernamePasswordToken token = new UsernamePasswordToken("username", "password"); token.setRememberMe(true); try { // 执行认证 currentUser.login(token); System.out.println("认证成功!"); } catch (Exception e) { System.out.println("认证失败:" + e.getMessage()); } } } 总结以上是 Shiro 中几种常见的认证方式及其使用示例。Shiro 的灵活性使其能够轻松集成到各种应用程序中,并根据需求选择合适的认证方式。
  • [技术干货] Apache Shiro 框架介绍
    Apache Shiro 简介Apache Shiro 是一个强大且易于使用的 Java 安全框架,旨在简化应用程序的身份验证、授权、加密和会话管理。它提供了全面的安全功能,同时保持了简洁性和灵活性,适用于各种规模的应用程序,从小型独立应用到大型企业级系统。Shiro 的核心功能Shiro 的核心功能可以归纳为四个主要方面:认证(Authentication)、授权(Authorization)、加密(Cryptography) 和 会话管理(Session Management)。以下是对这些功能的详细介绍:1. 认证(Authentication)认证是验证用户身份的过程,确保用户是他们声称的那个人。Shiro 提供了灵活的认证机制,支持多种认证方式,如用户名/密码、数字证书、OpenID 等。特点:支持多种认证数据源(如数据库、LDAP、Active Directory 等)。支持多因素认证。易于集成到现有应用程序中。示例:UsernamePasswordToken token = new UsernamePasswordToken("username", "password"); Subject currentUser = SecurityUtils.getSubject(); currentUser.login(token); // 执行认证 2. 授权(Authorization)授权是控制用户访问权限的过程,决定用户可以执行哪些操作或访问哪些资源。Shiro 提供了细粒度的授权控制,支持基于角色、权限和实例级别的授权。特点:支持基于角色的访问控制(RBAC)。支持基于权限的访问控制(如细粒度的资源访问控制)。支持表达式语言(如 Shiro 的 OGNL 表达式)进行灵活的授权策略定义。示例:// 检查用户是否具有特定角色 if (currentUser.hasRole("admin")) { // 用户具有管理员角色,执行相应操作 } // 检查用户是否具有特定权限 if (currentUser.isPermitted("user:create")) { // 用户具有创建用户的权限,执行相应操作 } 3. 加密(Cryptography)Shiro 提供了对加密操作的支持,包括哈希(Hashing)、加密(Encryption)和解密(Decryption)。这使得应用程序能够安全地存储密码、敏感数据等。特点:提供多种哈希算法(如 MD5、SHA-256 等)。支持对称加密和非对称加密。提供密码盐(Salt)支持,增强安全性。示例:// 使用 MD5 哈希算法对密码进行哈希处理 String hashedPassword = new Md5Hash("password", "salt").toString(); 4. 会话管理(Session Management)Shiro 提供了完整的会话管理功能,类似于 HTTP 会话,但不限于 Web 环境。它允许应用程序在非 Web 环境中(如桌面应用、移动应用等)也能管理用户会话。特点:支持会话的创建、销毁、持久化等操作。提供会话超时、会话监听等功能。支持分布式会话管理(通过 Redis 等缓存系统)。示例:// 获取当前用户的会话 Session session = currentUser.getSession(); // 设置会话属性 session.setAttribute("key", "value"); // 获取会话属性 Object value = session.getAttribute("key"); Shiro 的其他特性除了上述核心功能外,Shiro 还具有以下特性:Web 支持:提供对 Web 应用程序的支持,包括过滤器、Servlet 集成等。缓存支持:可以与多种缓存系统(如 Ehcache、Redis 等)集成,提高性能。“记住我”功能:支持用户登录后的“记住我”功能,提升用户体验。集成 Spring:提供与 Spring 框架的紧密集成,方便在 Spring 应用中使用 Shiro。总结Apache Shiro 是一个功能强大且易于使用的 Java 安全框架,它提供了全面的身份验证、授权、加密和会话管理功能。通过使用 Shiro,开发者可以轻松地为应用程序添加安全功能,保护应用程序免受未经授权的访问和攻击。无论是小型独立应用还是大型企业级系统,Shiro 都是一个值得考虑的安全解决方案。
  • [问题求助] 【问题求助】 算法精英实战营
    提交作品后,经历2小时后报错:internal_error,中间没有出现任何超时现象,而在本地跑是没有问题的。想问下,这种情况,是华为云平台本身的问题,还是我提交的作品的问题呀?如果是我作品的问题,这种情况,是什么原因导致的呢?
  • [问题求助] 咨询开发GSL流程中指定工号转移CELL问题
    【问题来源】     内部测试环境功能测试 【问题简要】     在ICD V300R008C25版本的SCE开发GSL流程中,在呼叫转移类CELL中有一个指定工号转移CELL控件,当前我有这样一个需求,转指定工号,若该工号是示闲状态,那么直接转到该工号下,若该工号非示闲状态(包含未登录),那么客户这边需要播放排队音乐,不做设备溢出,直到坐席空闲接起或客户主动挂断,该控件是否可以实现这样的逻辑呢?目前自测下来,坐席是示闲或者非示闲状态下都走了该cell控件的失败出口,是否使用该cell控件需要跟哪个cell搭配使用呢?麻烦了,谢谢!【问题类别】     IVR(gsl)     【AICC解决方案版本】     AICC 版本:AICC 24.200     SCE 版本: ICD V300R008C25 【期望解决时间】     尽快 【日志或错误截图】
  • [体验官] CodeArts的Java版本和C/C++和ShellRemote是一个安装包吗?
    CodeArts的Java版本和C/C++和ShellRemote是一个安装包吗, 为啥我下载这三个的安装包,安装下来都是一个软件呢?
  • [技术干货] Java内存分析全面指南
    Java内存分析是诊断性能问题和内存泄漏的关键技能。下面我将从内存结构、分析工具、常见问题和实战案例等方面进行全面介绍。一、Java内存结构详解1. 堆内存(Heap)新生代(Young Generation)Eden区:新对象分配区域Survivor区(From/To):存活对象过渡区默认比例:Eden:From:To = 8:1:1老年代(Old Generation)存放长期存活对象大对象直接进入老年代元空间(Metaspace)存储类元数据取代永久代(PermGen)默认不限制大小(受物理内存限制)2. 非堆内存JVM栈:线程私有,存储栈帧本地方法栈:Native方法调用程序计数器:线程执行位置二、内存分析工具1. JDK自带工具工具命令用途jpsjps -lvm查看Java进程jstatjstat -gcutil <pid> 1000实时GC统计jmapjmap -heap <pid>堆内存快照jhatjmap -dump:format=b,file=heap.hprof <pid> + jhat heap.hprof堆转储分析jstackjstack -l <pid>线程堆栈分析2. 图形化工具VisualVM:多功能监控/分析JConsole:基础监控Eclipse MAT:专业堆分析JProfiler:商业级分析工具Arthas:阿里开源的在线诊断工具3. 生产环境推荐Prometheus + Grafana:时序监控HeapHero:自动化堆分析GCeasy:GC日志分析三、常见内存问题分析1. 内存泄漏(Memory Leak)特征:堆内存持续增长Full GC后内存不释放OOM错误诊断步骤:获取堆转储:jmap -dump:live,format=b,file=leak.hprof <pid>使用MAT分析查找"Accumulation Point"(积累点)检查大对象和GC Roots引用链2. 内存溢出(OOM)常见类型:java.lang.OutOfMemoryError: Java heap space:堆内存不足java.lang.OutOfMemoryError: Metaspace:元空间不足java.lang.OutOfMemoryError: Unable to create new native thread:线程过多解决方案:增加对应区域内存(-Xmx, -XX:MaxMetaspaceSize)优化代码(减少对象创建)调整线程池配置3. GC相关问题症状:应用停顿(STW时间过长)CPU使用率高(GC线程占用)吞吐量下降分析方法:收集GC日志:-Xloggc:/path/to/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps使用GCeasy或GCEViewer分析四、实战分析案例案例1:内存泄漏分析场景:Web应用运行一段时间后OOM步骤:获取堆转储使用MAT打开,选择"Leak Suspects"报告发现HashMap占用了80%内存检查引用链发现静态Map缓存未清理解决方案:改用WeakHashMap或添加清理逻辑案例2:元空间溢出场景:动态生成类导致Metaspace不足解决:增加Metaspace大小:-XX:MaxMetaspaceSize=256m使用jcmd <pid> VM.metaspace监控优化动态类生成逻辑案例3:GC频繁症状:Young GC每分钟超过20次调优:增加新生代大小:-Xmn512m调整Survivor比例:-XX:SurvivorRatio=6优化短命对象创建五、高级分析技巧1. 堆外内存分析使用NMT(Native Memory Tracking):-XX:NativeMemoryTracking=detailjcmd <pid> VM.native_memory detail检查DirectByteBuffer使用2. 线程堆栈分析jstack <pid> > thread.txt查找死锁(deadlock)和阻塞(blocked)线程结合top -Hp <pid>查找CPU高的线程3. 内存分配分析使用JFR(Java Flight Recorder):-XX:StartFlightRecording=duration=60s,filename=alloc.jfr分析对象分配热点
  • [问题求助] CodeArts IDE for java 什么时候支持 java21 版本呢
    CodeArts IDE最新只能选择 17 的特性,什么时候支持 21
  • [问题求助] vxml开发在 菜单层按#报错
    图1是按键的方法。按#的时候报错  图2是报错日志(#是有设置菜单)
  • [问题求助] 开发GSL流程中使用Web接口直接访问Cell中如何解析返回的json对象中嵌套的json对象
    【问题来源】     内部测试环境功能测试 【问题简要】     在ICD V300R008C25版本的SCE开发GSL流程中,在外接功能类CELL中有一个Web接口直接访问CELL控件,调用的Get接口返回的json对象中有一个嵌套的json对象,如何获取到该嵌套的json整体对象呢,示例:{"type":"test1","values":{"key1":"value1","key2":"value2"}},想直接获取到values对应的整体的对象值{"key1":"value1","key2":"value2"},这种能支持获取到吗?我了解到的若获取key1或者key2对应的值,使用>符号来进行解析,如果是整体呢?麻烦了谢谢!【问题类别】     IVR(gsl)     【AICC解决方案版本】     AICC 版本:AICC 24.200     SCE 版本: ICD V300R008C25 【期望解决时间】     尽快 【日志或错误截图】
  • [问题求助] 算法精英实战营-第二十四期-问题求助
    有2个问题需要求助:1.压缩包内需要存放整个项目还是单独的文件就可以呀?2.上传作品后得分为-1,是因为编译没通过吗?还是其他什么原因呀?
  • [技术干货] 解析Java根基:Object类核心方法
    Object类常见方法解析在Java编程中,Object类是所有类的根类,它包含了许多实用的方法,这些方法在不同的场景下发挥着重要作用。下面我们来详细了解一下Object类中的一些常见方法。1. toString方法toString方法是用于将对象转换为字符串表示形式的方法。在默认情况下,toString方法返回的结果是类名加上@符号,再跟上该对象对应哈希码的十六进制表示。例如,当我们打印一个对象时,如果没有重写toString方法,就会得到类似这样的结果:com.example.MyClass@12345678。然而,在实际开发中,我们通常需要根据对象的具体属性来定制它的字符串表示形式,以便更清晰地展示对象的信息。这时,我们就需要重写toString方法。比如,对于一个表示学生信息的类Student,我们可以这样重写toString方法:public class Student {    private String name;    private int age;     // 构造方法和其他方法省略     @Override    public String toString() {        return "Student{name='" + name + "', age=" + age + "}";    }}这样,当我们打印一个Student对象时,就会得到包含学生姓名和年龄信息的字符串。2. equals和hashcode方法equals方法和hashcode方法在对象比较和集合操作中起着关键作用。equals方法:在进行对象比较的时候,我们通常会使用equals方法。如果不重写equals方法,默认情况下它会根据对象的地址进行比较,即只有两个引用指向同一个对象时,它们才被认为是相等的。但在很多实际场景中,我们需要根据对象的属性和方法来判断它们是否相等。例如,对于两个表示学生信息的对象,如果它们的姓名和年龄都相同,我们就认为这两个学生是相等的。这时,我们就需要重写equals方法,如下所示:@Overridepublic boolean equals(Object obj) {    if (this == obj) return true;    if (obj == null || getClass()!= obj.getClass()) return false;    Student student = (Student) obj;    return age == student.age && Objects.equals(name, student.name);}hashcode方法:hashcode方法用于返回对象的哈希码值。哈希码是一个整数,它在许多数据结构中都有重要作用,比如HashMap和HashSet。当我们对对象进行存储和查找操作时,哈希码可以帮助我们快速定位对象的位置。需要注意的是,当我们重写equals方法时,通常也需要同时重写hashcode方法。这是因为如果两个对象根据equals方法判断是相等的,那么它们的哈希码也必须相等。如果不重写hashcode方法,可能会导致哈希冲突的概率增大。例如,在HashMap中,如果只重写了equals方法而没有重写hashcode方法,那么具有相同属性的两个对象可能会被存储在不同的哈希桶中,这会使得一些哈希桶中的对象个数过多,从而降低搜索效率。另外,如果只重写了hashcode方法而没有重写equals方法,当发生哈希冲突时,会导致对象的安全性受到威胁。因为在哈希冲突的情况下,不同的对象可能会被存储在同一个哈希桶中,如果不重写equals方法,就无法正确判断这些对象是否真的相等,从而可能导致一些错误的结果。3. wait、notify和notifyAll方法wait、notify和notifyAll方法是用于线程间通信的方法,它们只能在同步代码块或同步方法中使用。 wait方法:当一个线程调用某个对象的wait方法时,它会解除对该对象的锁,并进入等待状态,直到其他线程调用该对象的notify或notifyAll方法来唤醒它。例如,在生产者-消费者模型中,当消费者发现缓冲区为空时,它会调用缓冲区对象的wait方法进入等待状态,直到生产者生产了新的数据并调用notify或notifyAll方法来唤醒它。notify方法:notify方法用于唤醒陷入等待状态的某一进程。它会随机选择一个正在等待该对象锁的线程,并将其唤醒。需要注意的是,notify方法只会唤醒一个线程,如果有多个线程在等待,那么具体唤醒哪个线程是不确定的。notifyAll方法:notifyAll方法用于唤醒陷入等待状态的所有进程。它会唤醒所有正在等待该对象锁的线程,这些线程会竞争获取对象的锁,然后继续执行。在这个示例中,生产者线程先生产数据,然后通过notify方法唤醒消费者线程,消费者线程在收到通知后开始消费数据。Java为什么被称为平台无关性语言Java实现平台无关性的核心机制在于JVM(Java虚拟机)的中间层设计。当Java源代码通过javac编译器生成字节码文件(.class文件)后,这些包含平台中立中间代码的文件可以在任何安装有对应平台JVM的设备上运行。各个操作系统虽然使用不同的机器指令集,但通过针对特定平台实现的JVM,字节码会被实时转换为所在系统的本地机器指令执行。这种"一次编译,到处运行"的特性,使得开发者无需针对不同操作系统修改源代码,JVM作为抽象层有效隔离了底层硬件和操作系统的差异。= =和equals有什么区别?== 和 equals 的区别基本概念== 是一个操作符,用于比较两个变量的值或引用。equals 是 Object 类的一个方法,用于比较两个对象的内容。如果 equals 方法没有被重写,它的默认行为与 == 相同。比较规则对于基本数据类型(如 int、char 等):== 比较的是变量的值。例如:int a = 5;int b = 5;System.out.println(a == b); // 输出 true对于对象类型(如 String、Integer 等):== 比较的是对象的引用(即内存地址)。例如:String str1 = new String("hello");String str2 = new String("hello");System.out.println(str1 == str2); // 输出 falseequals 方法比较的是对象的内容。例如:System.out.println(str1.equals(str2)); // 输出 trueequals 方法的重写默认情况下,equals 方法比较的是对象的引用(与 == 相同)。如果类重写了 equals 方法,则可以根据自定义的逻辑比较对象的内容。例如,String 类重写了 equals 方法,用于比较字符串的内容。重写 equals 方法时,通常需要同时重写 hashCode 方法,以确保对象在哈希表等数据结构中的行为一致。equals()与hashcode()equals和hashCode属于Object的方法,equals默认情况下和==等效,hashCode()是根据一定的规则根据对象返回的值,比如对象的地址,经过处理,返回哈希值在进行对象的比较的时候,可以进行自定义比较方法,需要重写equals方法,为了使HashMap、HashSet等方法正常存储,还需要对HashCode()进行重写HashMap存储对象的时候,会先调用HashCode方法进行比较,当HashCode值相等的时候,再使用equals方法再进行比较Student对象,有name和height两个成员变量,使用HashSet存储两个name和height相等的对象,判断Student对象是否相等就看这两个变量,当没有重写HashCode的时候,存储到HashSet中也会当作两个不同的对象,调用HashCode方法,因为没有重写,默认哈希值不会相同,所以会被认为两个对象不相同解决方法就是重写HashCode(),最简单的就是返回两个变量哈希值的积equals() 与 hashCode() 的核心机制equals() 和 hashCode() 是定义在 java.lang.Object 类中的基础方法。equals() 方法:默认实现与 == 运算符等价,用于判断两个对象是否严格相等(即是否指向同一内存地址)。开发中需根据业务需求重写此方法,以实现自定义的相等性逻辑。hashCode() 方法:默认返回对象的内存地址经过哈希算法处理后的整数值。其作用是为对象生成一个紧凑的哈希标识,主要用于快速定位数据存储位置(如哈希表)。集合框架中的关键作用当使用 HashMap、HashSet 等基于哈希的集合存储对象时:哈希优先原则:集合会先调用对象的 hashCode() 方法计算哈希值,若哈希值冲突,则进一步通过 equals() 方法比较对象的实际内容。一致性要求:若重写了 equals(),必须同步重写 hashCode()。若二者逻辑不一致(例如两个对象通过 equals() 判断相等,但 hashCode() 返回不同值),会导致集合无法正确维护元素唯一性,引发潜在 bug。Student 对象的典型场景分析假设存在如下 Student 类:public class Student {        private String name;        private int height;}未重写 hashCode() 的问题向 HashSet<Student> 中添加两个属性完全相同的对象时:Student s1 = new Student("Alice", 160);Student s2 = new Student("Alice", 160);Set<Student> students = new HashSet<>();students.add(s1);students.add(s2); // 实际执行结果:s2 会被视为新元素加入集合原因:默认的 hashCode() 实现基于对象内存地址,两次 new 操作生成的 s1 和 s2 地址不同,导致哈希值冲突被判定为不同对象。解决方案:重写 hashCode()通过覆盖 hashCode() 方法,将关键字段纳入哈希值计算:@Overridepublic int hashCode() {return Objects.hash(name, height); // 或简化为 name.hashCode() * 31 + height}原理:将 name 和 height 的哈希值按规则组合,确保内容相同的对象生成相同的哈希码,从而解决集合存储异常问题。重载和重写重载(Overloading)重载指的是在同一个类中,允许存在多个方法名相同但参数列表不同的方法。这些方法的参数个数、顺序或类型必须有所区别。通过这种方式,可以为同一操作提供多种不同的实现方式,以适应不同的输入需求。特点:方法签名不同:方法名相同,但参数列表(参数的个数、顺序或类型)不同。编译时决定:重载的解析是在编译期间完成的,编译器根据调用方法时提供的参数类型来决定具体调用哪一个重载的方法。静态绑定:由于在编译时已经确定了调用的具体方法,因此重载属于静态绑定。重写(Overriding)重写是指子类对父类中已有的方法进行重新定义,方法名、参数列表以及返回类型都必须与父类中的方法完全一致。通过重写,子类可以改变父类方法的行为,以适应子类的特定需求。特点:方法签名相同:方法名、参数列表和返回类型必须与父类中的方法完全一致。运行时决定:重写的解析是在运行期间完成的,具体执行哪一个方法取决于对象的实际类型。动态绑定:由于在运行时才确定调用的具体方法,因此重写属于动态绑定。抽象和接口抽象类与接口的对比分析【核心概念】抽象类使用 abstract 关键字修饰,用于定义具有部分实现的基类;接口使用 interface 关键字声明,作为纯粹的行为契约。二者在代码组织层面存在本质区别:【抽象类的应用】当多个类存在代码复用需求时,通过抽象类进行逻辑抽取可提升代码简洁性与可维护性。其核心价值体现在:代码复用:将公共实现逻辑提取到抽象基类中,子类通过继承复用代码强制约束:通过抽象方法(public abstract 修饰)强制子类实现特定功能类型标识:表达严格的继承关系,体现"是"(is-a)的语义特征修改时只需调整抽象基类即可影响所有子类,但需注意普通类也能实现类似效果,抽象类的核心区别在于其无法实例化并具有强制约束力。【接口的演进】接口作为规范标准,体现"具有...能力"(like-a)的语义特征,其发展历经多个阶段:传统接口(Java 7-)仅包含隐式 public abstract 修饰的抽象方法只允许声明 public static final 常量增强接口(Java 8+)引入 default 方法(含具体实现)支持静态方法定义允许定义私有方法(Java 9+)【实现规范】类实现接口时需遵循:必须实现所有抽象方法(未实现则类需声明为 abstract)可选择性覆盖 default 方法常量字段自动继承且不可修改支持多接口实现,体现灵活的组合特性Finalfinal 是 Java 的关键字,用于限制类、方法或变量的可修改性,具体用法如下:修饰类当类被 final 修饰时,该类不能被继承。核心目的:防止通过子类继承修改原有类的行为,常用于保护核心 API 的稳定性。示例:public final class String { ... } // String 类不可被继承修饰方法当方法被 final 修饰时,该方法不能被子类重写。核心目的:确保关键方法的行为不被破坏,常用于模板方法模式中的固定步骤。示例:public class Parent {        public final void lock() { ... } // 子类无法重写 lock()}修饰变量基本类型变量:变量值初始化后不可修改,成为常量。示例:final int MAX_VALUE = 100;// MAX_VALUE = 200; // 编译报错引用类型变量:引用类型变量变量指向的内存地址不可变,但对象内部状态可以修改。示例:final List list = new ArrayList<>();list.add("Java"); // 允许修改内容 // list = new LinkedList<>();  编译报错(地址不可变) static final 联合使用 static final 修饰的变量为全局常量,在类加载时初始化。 优势: 编译时常量(如字符串字面量)会被 JVM 优化,直接存入常量池,提升访问效率。 示例: public static final String VERSION = "1.0";不可变设计的经典案例:String 类的不可变性: String 类被 final 修饰,禁止继承。 内部存储数据的 char[] value 数组被 private final 修饰,且不提供修改方法(如 setter)。 所有修改操作(如 substring、replace)均返回新对象,原对象不变。 优势: 线程安全(无需同步锁)。 哈希值可缓存,提升性能。多线程场景下的应用使用 final 修饰变量可确保该变量的值在线程间可见且不可变,避免数据竞争问题。示例:        public class SafePublication {                private final int safeValue; // 安全发布,防止指令重排序                public SafePublication(int value) {                this.safeValue = value;        }}异常的理解异常是指在程序运行过程中可能发生的非正常事件,这类事件会中断程序的正常执行流程。通过异常处理机制,开发者可以捕获并处理这些异常,在保障程序核心功能不受致命影响的前提下完成错误恢复操作。异常的种类Java 的异常类继承体系以 Throwable 为顶层父类,具体分为两类:Error(错误)由 JVM 或底层系统资源引发的严重问题(如 StackOverflowError 栈溢出、OutOfMemoryError 内存耗尽),属于不可恢复的致命错误。Exception(异常)可被捕获处理的非致命性问题,进一步细分为:编译时异常(Checked Exception)编译器强制要求处理的异常类型,如 FileNotFoundException(文件未找到)、IOException(输入输出异常)。运行时异常(Runtime Exception)由代码逻辑缺陷引发的异常,编译器不强制处理,如 NullPointerException(空指针异常)、IndexOutOfBoundsException(数组越界异常)。thow和throws的区别throw是在方法内部使用,进行抛出异常,是一个动作。throws是在方法声明的后面,表示可能会抛出的异常,是一种声明那么直接try-catch不就行了,为什么还要抛出异常呢?首先我们要分情况说明,如果该异常可以被try-catch处理的话,就可以直接try-catch,比如说虽然A方法抛出了一个异常,但是基于他异常处理的方法在其他地方,并不需要处理,不需要处理的情况下,就抛出异常给调用者。其次,当调用的几个方法中都存在相同的异常,如果在这几个方法内部进行try-catch的话,会使得整体变得冗余,可以让调用方统一进行捕获异常,并进行处理,简洁化。最后还有可能就是上层调用方处理String不可变类的核心优势线程安全:String的不可变性天然规避了多线程同步问题,无需额外锁机制即可保证线程安全。哈希缓存:首次计算哈希值后缓存,作为Map键时无需重复计算,显著提升查找效率(如HashMap的键比较场景)。字符串创建与常量池机制共享优化:常量池通过引用复用已存在的字符串,避免重复创建。例如:String s1 = "hello"; // 常量池新建对象(若不存在)String s2 = "hello"; // 直接引用常量池现有对象对象创建示例:String a = new String("aa") + "bb"; 的详细过程:常量池新建"aa"对象(若不存在)。new String("aa")在堆中创建新对象(非池引用)。常量池新建"bb"对象(若不存在)。拼接生成"aabb",堆中创建新对象并可能更新常量池(若未存在),共创建了四个对象。字符串存储与内存限制底层结构:使用char[]存储,理论最大长度为Integer.MAX_VALUE(2³¹-1)。实际限制:编译期:最大长度为2¹⁶-2(65534,受CONSTANT_Utf8_info限制)。运行时:受JVM可用内存限制(如堆大小)。字符串常量池的演进Java 7前:位于永久代(PermGen),易引发OOM。Java 8+:迁移至元空间(Metaspace),降低内存溢出风险。字符串拼接效率对比String的+操作:String result = "a" + "b"; // 编译后等效于:String result = new StringBuilder().append("a").append("b").toString();隐含创建StringBuilder和临时String对象,效率较低。StringBuffer:直接操作内部可变数组,避免频繁对象创建。线程安全(同步开销),适合多线程场景。StringBuilder(补充建议):单线程下更高效(非线程安全)。————————————————              原文链接:https://blog.csdn.net/tulingtuling/article/details/147128918
总条数:691 到第
上滑加载中