`
yangyi
  • 浏览: 115343 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

利用JAAS及JNI实现在Java环境下的Unix/Linux权限认证

    博客分类:
  • Java
阅读更多
这篇随笔谈一谈如何在Java环境下利用Unix/Linux的用户名和密码对用户的权限作出过滤。为方便大家学习交流,本文中给出了源代码,借此抛砖引玉,欢迎大家对这个简单的登录模型做出改进或者设计出自己的技术方案。
由标题我们不难看出,与本文相关的知识点主要有3个:
1 JAAS这个解耦设计的多层验证方法(1.4后已归入Java核心库中)
2 应用JNI访问底层代码,及JNI中简单的类型匹配
3 在shadow模式下,Unix/Linux系统的用户验证

首先聊聊JAAS,顾名思义,JAAS由认证和授权两个主要组件组成。JAAS的交互点在LoginContext这个类里面,在构造LoginContext时,常常需要指定两个内容(还有其他默认的构造子重载形式),这两个内容是LoginModule的名字和Subject。
为使描述直观,先给出代码如下:
<!---->import java.security.Principal;

import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

public class MyLogin {
    
public MyLogin(){
        Subject subject 
= new Subject();
        subject.getPrincipals().add(
new Principal(){
            
public String getName() {
                
return "yiyang";
            }
        });
        subject.getPrivateCredentials().add(
"sh0w00f");
        
try {
            LoginContext lc 
= new LoginContext("mylogin",subject);
            lc.login();
        } 
catch (LoginException e) {
            e.printStackTrace();
        }
    }
    
public static void main(String[] args){
        
new MyLogin();
    }
}

先说LoginModule的名字,在系统属性java.security.auth.login.config中(或者在jre/lib/security/java.security)指定了LoginModule的配置文件,LoginModule在Java中被定义成一个接口,这个地方应用了面向对象的依赖倒置原则,使用了类似JDBC这样的SPI的机制来定制认证策略。根据用户在构造LoginContext时指定的LoginModule的名字Java在系统环境中找到对应的LoginModule配置文件,这个配置文件的最简单形式如下:
mylogin {
   UnixLoginModule required
};
这时当我们应用mylogin这个名字实例化LoginContext的时候,系统就会自动的找到UnixLoginModule这个LoginModule去处理。后面的required是一个flag标志,表示此次验证的关键性,有4个值可以选择,当选定required时则表示必须成功,由此我们就可以定义一系列的验证,形成一个过滤层,并根据不同的flag得出最后的结论,比如:我们可能希望我们的web用户只要通过数据库的验证,而不必通过操作系统的验证。
此外我们还可以设置一些其他的参数(以key=value的形式),而且实际上验证是两阶段提交的,并且可以通过回调函数的形式在具体的认证平台上做一些个性化Context设置。对这些JAAS细节感兴趣的朋友可以读相应的JAAS文档规范。
再说第二个参数Subject,这个主题封装了用户需要验证的信息,主要包括principal和公钥私钥两部分,详细的设置方法可以参考上面的代码。
lc.login返回了一个true或false表示了这次的验证是否成功。
当一个Subject成功login后,就可以通过这个Subject做一些特许的动作Subject.doAs,这些动作根据Subject中principal的不同在com.sun.security.auth.PolicyFile指定的配置文件做了定义,这部分是属于JAAS授权的内容,因为在我们的程序中暂时用不到,所以不做详细讨论了,我们仅仅根据login返回的true或false来决定用户是否可以登录我们的系统即可。

OK,说到这里,我们给出UnixLoginModule的实现代码:
<!---->import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

public class UnixLoginModule implements LoginModule {
    
private String usr, passwd;

    
public boolean abort() throws LoginException {
        
return false;
    }

    
public boolean commit() throws LoginException {
        System.out.println(
"Passing final confirmation\ndone");
        
return true;
    }

    
public boolean login() throws LoginException {
        ;
        
if (PasswdCheck.check(this.usr, this.passwd) == 0) {
            System.out.println(
"Your Login Succeed");
            
return true;
        }
        System.out.println(
"Your Login failed");
        
return false;
    }

    
public boolean logout() throws LoginException {
        
return false;
    }

    
public void initialize(Subject subject, CallbackHandler callbackHandler,
            Map
<String, ?> sharedState, Map<String, ?> options) {
        
this.passwd = (String) subject.getPrivateCredentials().iterator()
                .next();
        
this.usr = (String) subject.getPrincipals().iterator().next().getName();
    }
}

代码中的各个方法是LoginModule所定义的必须实现的方法。注意到代码中,我们应用了PasswdCheck.check(this.usr, this.passwd)来做最后的验证,这是因为对Unix系统用户的验证必须调用系统API才可以,而系统API是以C的形式提供的,因此我们需要借助JNI。现在我们看看PasswdCheck这个类:
<!---->public class PasswdCheck {
    
static{
        System.out.println(System.getProperty(
"java.library.path"));
        Runtime.getRuntime().load(
"/home/yiyang/eclipse/workspace/JAAS/libpasswd.so");
    }
    
    
public native static int check(String usr, String passwd);
    
}

在这里用到了JNI来调用底层的用户名密码验证方案,为此我们需要构造出libpasswd.so这个库。
一步一步来:
1 用javah生成JNI的头文件:
javah PasswdCheck
得到如下代码:
<!---->/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
<jni.h>
/* Header for class PasswdCheck */

#ifndef _Included_PasswdCheck
#define _Included_PasswdCheck
#ifdef __cplusplus
extern 
"C" {
#endif
/*
 * Class:     PasswdCheck
 * Method:    check
 * Signature: (Ljava/lang/String;Ljava/lang/String;)I
 
*/
JNIEXPORT jint JNICALL Java_PasswdCheck_check
  (JNIEnv 
*, jclass, jstring, jstring);

#ifdef __cplusplus
}
#endif
#endif

现在把头文件中定义的函数实现:
在jni.h这个头文件中定义了jni和c之间的类型关系,通过分析,用户名密码的字符串可以通过如下函数获取:
char * username =(*env)->GetStringUTFChars(env, usr, NULL);
char * password =(*env)->GetStringUTFChars(env, psw, NULL);
其他的简单型别很多被直接typedef了。
我们真对生成的头文件,实现如下:
<!---->#include "PasswdCheck.h"//生成的头文件
#include "pwd.h"//getspnam
#include "stdio.h"
#include 
"unistd.h"//crypt必需
#include "shadow.h"//getspnam
#define _XOPEN_SOURCE//crypt必需

JNIEXPORT jint JNICALL Java_PasswdCheck_check
  (JNIEnv 
* env, jclass jc, jstring usr, jstring psw){
    
char * username =(*env)->GetStringUTFChars(env, usr, NULL);
    
char * password =(*env)->GetStringUTFChars(env, psw, NULL);
    struct spwd 
* sp = getspnam(username);
    
char* p;
    p 
= crypt(password, sp->sp_pwdp);
    
return strcmp(sp->sp_pwdp,p);
}

上面的实现中的结构体spwd定义如下:

<!---->struct spwd {
      
char        *sp_namp; /* user login name */
      
char        *sp_pwdp; /* encrypted password */
      
long int        sp_lstchg; /* last password change */
      
long int        sp_min; /* days until change allowed. */
      
long int        sp_max; /* days before change required */
      
long int        sp_warn; /* days warning for expiration */
      
long int        sp_inact; /* days before account inactive */
      
long int        sp_expire; /* date when account expires */
      unsigned 
long int    sp_flag; /* reserved for future use */
}

getspnam函数可以获取一个被单向加密后的密码(有4种可选加密形式)
crypt函数把我们的原始密码按相同密钥和算法加密后,即可通过比较加密后字符串的形式获取是否密码正确的信息。需要主义的是只有在使用shadow机制的系统中才应用getspnam,如果/etc/passwd直接描述了密码,则可以通过函数getpwnam来获取(或者直接解析文本),这时一般采用的是13位的DES加密,问题变得简单。
在编写完实现后通过命令
gcc -lcrypt PasswdCheck.c -shared -o libpasswd.so
进行编译,把这个库cp到/usr/lib(或其他ld_library_path)下就可以用平台相关的方式System.loadLibrary加载,否则就要用系统绝对路径名了(利用System.load)
因为只有root能获取到getspnam,所以我们只能这样来执行我们的java进行验证,sudo java MyLogin (yiyang is in group wheel defined in /etc/sudoers)
否则将得到如下出错信息:
<!---->#
# An unexpected error has been detected by Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0xb7ef95ad, pid=9726, tid=3084450720
#
# Java VM: Java HotSpot(TM) Client VM (1.6.0_03-b05 mixed mode)
# Problematic frame:
# C  [libpasswd.so+0x5ad]  Java_PasswdCheck_check+0x61
#
# An error report file with more information is saved as hs_err_pid9726.log
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
#

当然,如果我们不用JNI,而采用Web Services(具体方法见笔者上一篇blog: http://yangyi.blogjava.net)那么可以通过set suid的形式定制一个进程了(不过这已经是另一个话题),毕竟用root启动tomcat不是很让人放心:
chown root XXX
chmod +s XXX
Anyway, 至此通过JAAS认证Unix用户的基本思路就描述完了,读者可以填补其中的漏洞并把JAAS用到自己的工作场景中。

分享到:
评论

相关推荐

    Java JAAS安全认证 demo

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于实现用户身份验证和权限管理的核心组件。这个"Java JAAS安全认证 demo"是一个示例项目,旨在帮助开发者理解如何在Java应用中实施安全认证...

    java JAAS登陆验证

    JAAS提供了一种标准的方式来实现这一目标,允许开发者在不深入了解底层安全机制的情况下,构建安全的应用程序。 **JAAS 登录验证机制** JAAS的核心概念是登录模块(LoginModule),它是处理用户身份验证逻辑的组件...

    基于JAAS的Java安全编程

    Java 2平台的安全框架主要关注代码来源的认证和控制,而JAAS则在此基础上增加了对代码执行者的认证和授权,从而实现了更加精细的访问控制。 随着互联网安全问题的日益凸显,JAAS作为一种强大的安全机制,在Java EE ...

    在JAAS基础上的Java安全编程

    ### 在JAAS基础上的Java安全编程 #### 一、引言 随着互联网技术的发展和企业对数据安全的重视,Java作为一种广泛应用的编程语言,在安全领域扮演着越来越重要的角色。JAAS(Java Authentication and Authorization...

    JAAS简介及示例代码

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全性的核心组件,它为开发者提供了一种机制来处理用户身份验证和权限控制。这个服务允许应用程序执行基于角色的安全性,这意味着用户的...

    JAAS灵活的Java安全机制

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于实现用户认证和权限授权的一个核心组件。它为开发者提供了一种灵活的方式来实现安全控制,确保只有经过验证和授权的用户能够访问敏感资源或...

    JAAS认证(mac版tomcat)

    3. **配置JAAS配置文件**:在Mac系统中,JAAS配置文件通常位于`/Library/Java/JavaVirtualMachines/jdk版本.jdk/Contents/Home/lib/security/java.policy`。你需要在这个文件中添加你的应用程序和LoginModule的配置...

    jaas规范实现代码

    Java Authentication and Authorization Service (JAAS) 是 Java 平台中用于安全管理的重要组件,它提供了一种框架,使得应用程序可以进行用户身份验证和权限控制。在本文中,我们将深入探讨 JAAS 的核心概念、工作...

    利用JAAS实现简单的页面验证与授权

    ### 利用JAAS实现简单的页面验证与授权 #### JAAS简介 Java Authentication and Authorization Service (JAAS) 是 Java 平台提供的一种安全框架,它主要用于实现应用级别的身份验证和授权服务。JAAS 的出现是对 ...

    java软件包文件 jaas.jar

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全性的关键组件,它提供了一种框架,用于在Java应用程序中实现认证(Authentication)和授权(Authorization)。`jaas.jar` 文件是这个...

    JAAS认证与授权教程

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全性的关键组件,它提供了一种框架,使得应用程序能够实现用户身份验证和权限管理。在这个教程中,我们将深入探讨JAAS的核心概念、工作...

    JAAS.rar_Action!_Jaas Kerber_jaas KERBEROS_kerberos Java_login.a

    _Jaas_Kerber_jaas_KERBEROS_kerberos_Java_login.a"压缩包文件包含了关于如何在Java环境中使用JAAS实现Kerberos登录和委托的示例代码。Kerberos是一种广泛使用的网络身份验证协议,它提供了强大的安全性,通过一次...

    JAAS(JAVA验证与授权服务)完整示例

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全验证和权限管理的重要框架。它为应用程序提供了一种标准化的方式来处理用户身份验证和权限分配,从而确保只有经过验证的用户才能访问受...

    JAAS简介及实例

    Java Authentication and Authorization Service (JAAS) 是Java平台提供的一种安全框架,用于实现用户身份验证和权限管理。这个框架使得开发者可以轻松地在Java应用程序中集成安全性,而不必深入理解底层的复杂安全...

    JAAS灵活的Java安全机制.docx

    Java Authentication and Authorization Service (JAAS) 是Java平台上用于管理和实施安全策略的重要组件,它提供了一种灵活且可...对于任何需要在Java环境中实现安全功能的开发者而言,理解和掌握JAAS都是至关重要的。

    kerberos的java实现

    在Java中,我们可以使用`KerberosLoginModule`实现Kerberos认证。这个模块是Java的登录框架(JAAS,Java Authentication and Authorization Service)的一部分,允许用户根据策略文件配置认证流程。 - **配置JAAS...

    JAAS 认证服务

    JAAS(Java Authentication and Authorization Service)是Java平台提供的一种安全框架,用于实现客户端应用程序的安全认证和授权管理。它为Java应用提供了一种灵活的方式来实现安全控制,使得开发人员能够轻松地在...

    JAAS:灵活的Java安全机制

    ### JAAS:灵活的Java安全机制 #### 一、引言 Java Authentication and Authorization Service (JAAS, Java验证和授权API)为Java应用程序提供了一种扩展标准Java 2安全模型的方法。通过添加验证主题(即用户或其他...

    java写的一个权限控制的插件

    4. **过滤器与拦截器**:为了实现权限控制,插件可能利用了Servlet Filter或者Spring MVC的Interceptor机制,对请求进行预处理,判断用户是否有权限执行当前操作。 5. **动态权限分配**:在某些情况下,权限可能...

    Jaas in Action (java 安全)

    Java Authentication and Authorization Service (JAAS) 是Java平台中用于安全认证和授权的核心组件。它为开发者提供了一种标准的方式来实现用户身份验证和访问控制,从而确保应用程序的安全性。"Jaas in Action"这...

Global site tag (gtag.js) - Google Analytics