
1. 项目概述为什么Java安全实践是每个开发者的必修课最近在面试和带团队的过程中我发现一个挺普遍的现象很多Java开发者尤其是工作两三年的朋友对“安全”的理解还停留在“加个HTTPS”或者“用Spring Security配一下”的层面。一旦被问到“SSL握手的具体过程是怎样的”、“JAAS和Spring Security的认证授权机制底层有什么区别”或者遇到线上环境证书过期、密钥库配置错误导致服务不可用的问题时就显得有些手足无措。这其实挺危险的因为安全不是框架自动完成的魔法而是需要开发者深刻理解并正确实践的一系列技术决策。这个项目标题“深入理解Java安全实践SSL, HTTPS与JAAS认证授权”恰好切中了现代Java应用开发中最核心、也最容易出问题的三个安全领域传输安全、身份认证与访问控制。SSL/TLS是网络通信的基石HTTPS是其最广泛的应用而JAASJava Authentication and Authorization Service则是Java平台为认证授权提供的一套标准、可插拔的框架。把它们放在一起讲是因为一个健壮的应用安全体系必然是这三者的有机结合。你不可能在一个HTTP明文传输的通道上构建可靠的认证也不可能只依赖传输层安全而忽略应用层的细粒度授权。我打算用这篇长文把我这些年踩过的坑、调过的参数、以及从生产事故中总结的经验系统地梳理一遍。目标不是让你死记硬背面试八股而是真正理解这些技术背后的“为什么”并能在实际项目中无论是自研框架还是集成Spring Security都能做出合理的设计和精准的排错。我们会从最基础的密码学概念聊起一直深入到JDK和常见中间件的源码层面看看这些“黑盒”到底是怎么运转的。2. 核心概念拆解从密码学基础到Java安全架构在直接动手配置SSL或编写JAAS模块之前我们必须先统一语言理解支撑这些技术的底层原理。否则面对一堆配置错误和运行时异常你只能盲目尝试无法从根本上解决问题。2.1 密码学基石对称加密、非对称加密与摘要算法所有现代安全协议都建立在密码学之上。你可以把它们想象成一套构建数字世界“锁与钥匙”的规则。对称加密比如AES、DES加密和解密使用同一把密钥。它的优点是速度快适合加密大量数据。但关键问题在于如何安全地把这把“共享的密钥”交给通信的对方在互联网上直接发送密钥就像用明信片邮寄家门钥匙一样危险。非对称加密典型代表是RSA、ECC。它有一对密钥公钥和私钥。公钥可以公开给任何人用于加密数据但只有对应的私钥才能解密。私钥必须严格保密。这完美解决了密钥分发问题我可以把我的公钥给你你用公钥加密信息后发给我只有我的私钥能解开。但非对称加密的计算开销非常大比对称加密慢几个数量级。于是实际的协议如SSL/TLS采用了一种混合模式使用非对称加密来安全地协商一个临时的对称会话密钥。后续大量的数据传输则用这个高效的对称密钥来加密。这就兼顾了安全性和性能。摘要算法如SHA-256、MD5它接收任意长度的数据输出一个固定长度的“指纹”哈希值。关键特性是单向性和抗碰撞性你无法从哈希值反推出原始数据也极难找到两个不同的数据产生相同的哈希值。这主要用于验证数据完整性数据是否被篡改和密码存储不存明文密码只存其哈希值。注意MD5和SHA-1现在已被认为是不安全的因其已被发现有效的碰撞方法。在生产环境中至少应使用SHA-256。2.2 SSL/TLS与HTTPS不仅仅是“那把锁”很多人把HTTPS简单理解为“HTTP over SSL”这不够准确。更专业的说法是HTTPS是HTTP协议在TLS/SSL协议建立的加密通道上运行。SSLSecure Sockets Layer和它的继任者TLSTransport Layer Security才是负责建立这个安全通道的协议。TLS握手过程是其精髓我把它简化成几个核心步骤ClientHello客户端向服务器打招呼说“嗨我支持这些TLS版本和加密套件Cipher Suites这是我的随机数A。”ServerHello服务器回应“好的我们决定用这个TLS版本和这个加密套件这是我的随机数B还有我的证书里面包含我的公钥。”证书验证客户端验证服务器证书的合法性是否由可信CA签发、是否在有效期内、域名是否匹配等。这是信任的起点。密钥交换客户端生成一个“预主密钥”用服务器的公钥加密后发送过去。只有拥有对应私钥的服务器能解密它。生成会话密钥客户端和服务器利用随机数A、B和预主密钥各自独立计算出相同的主密钥和会话密钥。后续通信就用这个会话密钥进行对称加密。HTTPS就是在上述TLS通道建立成功后在这个加密的管道里跑普通的HTTP协议。所以你看到的https://开头的URL底层是先完成了复杂的TLS握手才开始了HTTP的请求响应。2.3 JAASJava平台的标准安全“插座”如果说SSL/TLS管的是“路”的安全那么JAAS管的就是“门”和“房间”的安全——即“谁可以访问”认证和“能做什么”授权。JAAS的核心设计思想是可插拔。它将认证过程抽象出来使得应用程序可以独立于底层的认证技术。你可以今天用Linux系统的用户名密码通过PAM模块明天换成LDAP后天换成Kerberos而你的业务代码几乎不用改动。它的核心组件包括Subject代表一个认证实体比如一个用户或服务。它持有这个实体的Principal身份如用户名和Credential凭证如密码、密钥。LoginModule执行认证逻辑的插件。一个应用可以配置多个LoginModule形成认证链。Configuration决定为某个应用使用哪些LoginModule。Policy和AuthPermission定义授权策略即某个Principal身份是否拥有执行特定操作如读写某个文件的权限。虽然现在Spring Security大行其道但很多底层机制比如Servlet容器的安全约束和传统企业级应用如WebLogic、WebSphere中的安全配置仍然深度依赖或兼容JAAS。理解JAAS是理解Java安全生态的重要一环。3. 深入Java中的SSL/TLS与HTTPS实践理解了原理我们来看在Java世界里怎么玩转它。这里面的坑多到可以写一本“避坑指南”。3.1 密钥与证书管理KeyStore与TrustStoreJava使用KeyStore和TrustStore本质也是KeyStore但用途不同来管理密钥和证书。这是最容易混淆和出错的地方。KeyStore密钥库存储你自己的私钥和对应的证书链。当你的Java应用作为服务器时你需要用它来向客户端证明你的身份。文件格式通常是JKSJava KeyStore或PKCS12.p12或.pfx。JKS是Java独有的而PKCS12是行业标准现在更推荐使用PKCS12。TrustStore信任库存储你信任的证书通常是受信任的CA证书颁发机构的根证书和中间证书。当你的Java应用作为客户端去连接其他HTTPS服务时用它来验证对方服务器的证书是否可信。它们的对应关系如下表所示角色存储内容用途常用系统属性服务器端自己的私钥和证书链在TLS握手时出示证书供客户端验证-Djavax.net.ssl.keyStore-Djavax.net.ssl.keyStorePassword客户端信任的CA证书TrustStore验证服务器发来的证书是否可信-Djavax.net.ssl.trustStore-Djavax.net.ssl.trustStorePassword实操心得在Docker容器或K8s环境中不要将包含密码的KeyStore文件直接打包进镜像。最佳实践是将KeyStore文件通过Secret卷挂载到容器内。密码通过环境变量或专门的Secret管理工具如HashiCorp Vault注入。在应用启动脚本中使用这些动态值来设置JVM系统属性。例如java -Djavax.net.ssl.keyStore/path/to/mounted/keystore.p12 \ -Djavax.net.ssl.keyStorePassword${KEYSTORE_PASSWORD} \ -Djavax.net.ssl.trustStore/path/to/truststore.jks \ -Djavax.net.ssl.trustStorePassword${TRUSTSTORE_PASSWORD} \ -jar your-app.jar3.2 代码层面实现HTTPS客户端与服务端光有配置还不够我们看看在代码里如何控制SSL行为。3.2.1 创建自定义的SSLContext大多数HTTP客户端如Apache HttpClient、OkHttp、Spring的RestTemplate都允许你注入一个自定义的SSLContext这是控制SSL行为的总开关。import javax.net.ssl.*; import java.io.InputStream; import java.security.KeyStore; public class CustomSSLContextFactory { public static SSLContext createSSLContext(String keyStorePath, String keyStorePass, String trustStorePath, String trustStorePass) throws Exception { // 1. 初始化KeyManagerFactory (用于客户端认证或服务器端出示证书) KeyManagerFactory kmf null; if (keyStorePath ! null) { KeyStore keyStore KeyStore.getInstance(PKCS12); try (InputStream is CustomSSLContextFactory.class.getResourceAsStream(keyStorePath)) { keyStore.load(is, keyStorePass.toCharArray()); } kmf KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, keyStorePass.toCharArray()); } // 2. 初始化TrustManagerFactory (用于验证对端证书) TrustManagerFactory tmf TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); KeyStore trustStore KeyStore.getInstance(JKS); try (InputStream is CustomSSLContextFactory.class.getResourceAsStream(trustStorePath)) { trustStore.load(is, trustStorePass.toCharArray()); } tmf.init(trustStore); // 3. 创建并初始化SSLContext SSLContext sslContext SSLContext.getInstance(TLS); // 通常使用TLS sslContext.init(kmf ! null ? kmf.getKeyManagers() : null, tmf.getTrustManagers(), new java.security.SecureRandom()); // 使用安全的随机数源 return sslContext; } }然后在配置HTTP客户端时使用这个SSLContext// 使用Apache HttpClient 5.x 示例 SSLContext sslContext CustomSSLContextFactory.createSSLContext(...); SSLConnectionSocketFactory sslSocketFactory new SSLConnectionSocketFactory(sslContext); PoolingHttpClientConnectionManager cm PoolingHttpClientConnectionManagerBuilder.create() .setSSLSocketFactory(sslSocketFactory) .build(); CloseableHttpClient httpClient HttpClients.custom() .setConnectionManager(cm) .build();3.2.2 处理“自签名证书”或内部CA证书在开发、测试或内网环境中我们经常使用自签名证书或内部CA签发的证书。这些证书默认不在JRE的信任库cacerts里客户端连接时会报SSLHandshakeException提示unable to find valid certification path to requested target。解决方法不是盲目地禁用证书验证绝对不要在生产环境这样做而是将你的自签名证书或内部CA的根证书导入到客户端的TrustStore中。# 假设你有一个自签名证书 server.crt # 1. 将证书导入到一个新的或已有的JKS信任库 keytool -import -alias my-internal-ca -keystore /path/to/custom-truststore.jks -file server.crt # 2. 运行客户端时指定这个自定义的trustStore java -Djavax.net.ssl.trustStore/path/to/custom-truststore.jks \ -Djavax.net.ssl.trustStorePasswordchangeit \ -jar your-client-app.jar3.2.3 服务器端配置以Spring Boot为例Spring Boot使得配置内嵌Tomcat的HTTPS变得非常简单。# application.yml server: port: 8443 ssl: enabled: true key-store: classpath:keystore.p12 # 或 file:/path/to/keystore.p12 key-store-password: your-keystore-password key-store-type: PKCS12 key-alias: your-alias # 如果keystore中有多个条目需要指定别名 # 可选客户端认证双向TLS # client-auth: need # 要求客户端提供证书 # trust-store: classpath:truststore.jks # trust-store-password: your-truststore-password重要提示key-store-password等敏感信息绝不能硬编码在配置文件中。应该使用环境变量或配置中心。在Spring Boot中可以这样写key-store-password: ${KEY_STORE_PASSWORD}然后通过环境变量KEY_STORE_PASSWORD传入。3.3 高级话题与性能调优3.3.1 会话恢复Session Resumption为了减少完整TLS握手的开销TLS提供了会话恢复机制。服务器可以将一次完整握手协商出的会话状态Session ID或Session Ticket缓存起来。在下次连接时客户端可以出示这个ID或Ticket直接恢复会话跳过非对称加密计算极大提升重连速度。在Java中SSLSessionContext用于管理这些会话。3.3.2 加密套件Cipher Suites选择与安全加固加密套件定义了握手和通信过程中使用的算法组合例如TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256。你应该禁用已知不安全的套件如包含NULL、EXPORT、DES、RC4、MD5的并优先使用前向保密Forward Secrecy的套件如以ECDHE开头的。在Tomcat的server.xml中可以配置Connector port8443 protocolorg.apache.coyote.http11.Http11NioProtocol maxThreads150 SSLEnabledtrue SSLHostConfig Certificate ... / Cipher TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 /Cipher /SSLHostConfig /Connector3.3.3 证书监控与自动续期生产环境最大的坑之一就是证书过期。Let‘s Encrypt的证书只有90天有效期。务必建立监控告警机制在证书到期前30天发出预警。并采用自动化工具如Certbot进行续期。续期后需要重新加载KeyStore或重启服务有些服务器支持热加载。4. JAAS认证授权实战从原理到集成现在我们把目光从传输层移到应用层。JAAS提供了一套标准的API但直接使用它略显繁琐我们通常会在其基础上构建或集成更上层的框架。4.1 实现一个自定义的LoginModule理解JAAS最好的方式就是自己写一个简单的LoginModule。假设我们要做一个基于数据库用户名密码的认证。import javax.security.auth.Subject; import javax.security.auth.callback.*; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; import java.io.IOException; import java.util.Map; import java.sql.*; public class DatabaseLoginModule implements LoginModule { private Subject subject; private CallbackHandler callbackHandler; private MapString, ? sharedState; private MapString, ? options; private String username; private boolean loginSucceeded false; Override public void initialize(Subject subject, CallbackHandler callbackHandler, MapString, ? sharedState, MapString, ? options) { this.subject subject; this.callbackHandler callbackHandler; this.sharedState sharedState; this.options options; } Override public boolean login() throws LoginException { // 1. 通过CallbackHandler获取用户凭证 Callback[] callbacks new Callback[2]; callbacks[0] new NameCallback(Username: ); callbacks[1] new PasswordCallback(Password: , false); try { callbackHandler.handle(callbacks); username ((NameCallback) callbacks[0]).getName(); char[] password ((PasswordCallback) callbacks[1]).getPassword(); ((PasswordCallback) callbacks[1]).clearPassword(); // 及时清理内存中的密码 // 2. 验证逻辑这里简化实际应从数据库查询并比较哈希值 // 假设有一个方法 verifyFromDatabase(username, password) if (verifyFromDatabase(username, new String(password))) { loginSucceeded true; return true; } else { throw new LoginException(Authentication failed); } } catch (IOException | UnsupportedCallbackException e) { throw new LoginException(Error obtaining user credentials: e.getMessage()); } } private boolean verifyFromDatabase(String user, String pass) { // 伪代码连接数据库查询对应用户的密码哈希值进行比对 // 重要永远不要在数据库中存储明文密码应存储加盐的哈希值。 // return BCrypt.checkpw(pass, hashedPasswordFromDB); return validUser.equals(user) validPass.equals(pass); // 示例 } Override public boolean commit() throws LoginException { if (!loginSucceeded) { return false; } // 认证成功将Principal身份添加到Subject中 subject.getPrincipals().add(new SimplePrincipal(username)); // 还可以添加Credential凭证或自定义的Role Principal // subject.getPublicCredentials().add(...); return true; } Override public boolean abort() throws LoginException { logout(); return true; } Override public boolean logout() throws LoginException { subject.getPrincipals().removeIf(p - p instanceof SimplePrincipal); loginSucceeded false; username null; return true; } // 一个简单的Principal实现 public static class SimplePrincipal implements java.security.Principal { private final String name; public SimplePrincipal(String name) { this.name name; } Override public String getName() { return name; } Override public boolean equals(Object o) { ... } Override public int hashCode() { ... } } }然后你需要在一个JAAS配置文件中声明这个模块比如myjaas.configMyDatabaseAuth { com.yourcompany.security.DatabaseLoginModule required; };运行程序时指定这个配置java -Djava.security.auth.login.configmyjaas.config YourApp。4.2 JAAS与Servlet容器集成在传统的Java Web应用中你可以在web.xml中配置安全约束并指定使用JAAS作为认证机制。web-app security-constraint web-resource-collection web-resource-nameProtected Area/web-resource-name url-pattern/admin/*/url-pattern /web-resource-collection auth-constraint role-nameADMIN/role-name /auth-constraint /security-constraint login-config auth-methodBASIC/auth-method !-- 或使用 FORM, CLIENT-CERT 等 -- realm-nameMyJAASRealm/realm-name /login-config security-role role-nameADMIN/role-name /security-role /web-app同时在Tomcat的conf/server.xml中配置一个JAAS RealmRealm classNameorg.apache.catalina.realm.JAASRealm appNameMyDatabaseAuth !-- 对应JAAS配置中的条目名 -- userClassNamescom.yourcompany.security.DatabaseLoginModule$SimplePrincipal roleClassNamescom.yourcompany.security.RolePrincipal /这样当用户访问/admin/*路径时容器就会触发JAAS认证流程。4.3 与Spring Security的关联与区别Spring Security是事实上的Java应用安全标准它功能强大且高度可定制。它并不直接等同于JAAS但可以与JAAS集成。Spring Security是一个全面的、声明式的安全框架。它提供了从认证表单、OAuth2、LDAP等、授权方法级、URL级、到防护CSRF、CORS等一系列功能。它有自己的安全上下文SecurityContextHolder和认证对象Authentication。JAAS是Java平台标准更底层主要提供可插拔的认证模块机制。Spring Security可以利用JAAS的LoginModule作为其认证源之一。你可以通过配置让Spring Security委托给一个JAAS的LoginModule进行认证Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jaasAuthentication() .loginConfig(classpath:myjaas.config) .loginContextName(MyDatabaseAuth) .authorityGranters(new SimpleAuthorityGranter()); // 用于将JAAS Principal转为Spring Security的GrantedAuthority } Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(/admin/**).hasRole(ADMIN) .anyRequest().authenticated() .and() .formLogin(); } }核心区别Spring Security是一个全栈解决方案而JAAS主要解决认证插拔的问题。在现代Spring Boot应用中你很少需要直接编写JAASLoginModule但理解它有助于你深度定制Spring Security或处理遗留系统集成。5. 生产环境常见问题与深度排查指南理论结合实践最后这部分是我认为最有价值的“踩坑实录”。很多问题在开发环境不会出现一到生产环境就让你头皮发麻。5.1 SSL/TLS相关问题排查问题1javax.net.ssl.SSLHandshakeException: No appropriate protocol现象客户端或服务器连接失败提示协议不合适。根因双方支持的TLS协议版本不匹配。例如Java 8默认可能禁用老旧的SSLv3而对方服务器只支持SSLv3或者你的客户端禁用了TLS 1.0/1.1而老旧的服务器只支持这些版本。排查检查JVM版本和默认安全策略。Java 8u161以后默认禁用了TLS 1.0和1.1。可以通过SSLSocket或SSLContext的getSupportedProtocols()和getEnabledProtocols()方法查看。使用命令行工具测试openssl s_client -connect example.com:443 -tls1_2指定协议版本。在线SSL检测工具如SSL Labs扫描服务器配置。解决客户端在创建SSLContext或设置系统属性时显式指定协议版本。注意不要为了兼容性而启用不安全的协议如SSLv3。SSLContext sslContext SSLContext.getInstance(TLSv1.2); // 明确使用TLS 1.2 sslContext.init(...);服务器在Web服务器配置中禁用不安全的协议只启用TLS 1.2和1.3。问题2sun.security.validator.ValidatorException: PKIX path building failed现象客户端连接HTTPS服务器时证书验证失败。根因客户端无法构建一条从服务器证书到可信CA根的完整信任链。可能原因服务器使用自签名证书。服务器证书由内部/私有CA签发但该CA的根证书未导入客户端的TrustStore。证书链不完整服务器未发送中间证书。排查用浏览器或openssl s_client -showcerts -connect example.com:443命令查看服务器发送的完整证书链。检查你的javax.net.ssl.trustStore指向的JKS文件是否包含了必要的根证书和中间证书。解决对于内部服务将内部CA的根证书导入客户端的TrustStore。确保服务器配置正确发送了完整的证书链包括中间证书。仅限测试实现一个绕过证书验证的TrustManager。警告此方法极度危险绝不可用于生产环境// *** 危险示例仅用于本地开发测试 *** TrustManager[] trustAllCerts new TrustManager[] { new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; SSLContext sc SSLContext.getInstance(TLS); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());问题3java.security.cert.CertificateException: No subject alternative names present现象证书中的域名与请求的实际主机名不匹配。根因证书是为www.example.com签发的但你访问的是example.com或IP地址。或者证书的SAN主题备用名称字段不包含你使用的主机名。解决确保访问的URL主机名与证书的CN通用名称或SAN列表中的条目完全匹配。如果是内部测试可以考虑使用HttpsURLConnection的HostnameVerifier来绕过主机名检查同样生产环境禁用。// *** 危险示例仅用于测试 *** HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) - true);5.2 JAAS与集成问题排查问题1javax.security.auth.login.LoginException: No LoginModules configured现象启动应用时抛出此异常。根因JVM没有找到JAAS配置文件或者配置文件中没有对应名称的条目。排查确认-Djava.security.auth.login.config参数路径是否正确文件是否存在。确认配置文件中的条目名称如MyDatabaseAuth与代码中LoginContext初始化时使用的名称完全一致大小写敏感。解决仔细检查配置文件和系统属性。问题2认证成功但获取不到角色/权限现象用户能登录但访问需要特定角色的资源时被拒绝。根因LoginModule的commit()方法中没有将代表角色的Principal对象添加到Subject中。或者在Servlet容器或Spring Security中没有正确配置如何从JAASPrincipal映射到框架的角色GrantedAuthority。排查调试你的LoginModule确认在commit()阶段是否添加了角色Principal。检查Tomcat的JAAS Realm配置或Spring Security的AuthorityGranter确保它能正确识别你自定义的Principal类型并将其转换为角色。解决确保认证流程结束后Subject中包含了身份和角色信息并且下游框架能正确理解这些信息。5.3 性能与运维问题问题HTTPS服务性能明显低于HTTP根因TLS握手和对称加密解密带来额外的CPU开销。优化方向启用会话恢复确保服务器和客户端都支持并启用了TLS会话恢复Session ID或Session Ticket减少重复握手。使用更高效的加密套件优先使用AES-GCM支持硬件加速而非CBC模式的套件。启用TLS 1.3其握手过程比1.2更快。优化证书使用ECC椭圆曲线证书代替RSA证书。ECC密钥更短计算更快且安全性相当。考虑硬件加速对于超高流量场景可以考虑使用支持SSL硬件加速的负载均衡器如Nginx、HAProxy或网卡。监控与调优监控服务器的CPU使用率、TLS握手速率、当前会话数等指标。根据监控数据调整连接池、线程池大小。问题证书过期导致服务中断预防自动化使用Let‘s Encrypt等提供自动续期服务的CA并搭配Certbot等工具实现无人值守续期。监控告警对服务器证书过期时间进行监控提前30天、15天、7天发出多级告警。可以使用Prometheus的ssl_exporter或黑盒探测。流程化建立证书管理流程明确证书的申请、部署、更新和吊销责任人。写到这里关于Java中SSL、HTTPS和JAAS的核心实践要点已经覆盖得比较全面了。安全是一个持续的过程而不是一次性的配置。最关键的是建立一种“安全意识”理解你使用的每一个安全配置项背后的含义定期审查和更新你的加密套件、协议版本和证书并在设计系统时就将安全考虑在内。比如在新的微服务架构中除了传输层的TLS服务间的认证授权如使用JWT或mTLS也同样重要这可以看作是JAAS思想在分布式系统中的延伸。