Java认证和授权服务 JAAS 之 认证

来源:互联网 发布:淘宝交易款临时冻结 编辑:程序博客网 时间:2024/05/22 08:17

LoginModule

    它是认证服务器端的实现,用于验证客户端的信息,实现者实现 javax.security.auth.spi.LoginModule 接口的 login、commit、abort、logout 方法来完成用户的登录和登出操作,示例代码:


package jaas;import javax.security.auth.Subject;import javax.security.auth.callback.*;import javax.security.auth.login.FailedLoginException;import javax.security.auth.login.LoginException;import javax.security.auth.spi.LoginModule;import java.security.Principal;import java.util.Map;public class MyLoginModule implements LoginModule {    // username and password    private String username;    private char[] password;    // the authentication status    private boolean userPwdSucceeded = false;    private boolean commitSucceeded = false;    // user's Principal    private Principal userPrincipal;    // initial state    private Subject subject;    private CallbackHandler callbackHandler;    /**     * Initialize this <code>LoginModule</code>.     */    public void initialize(Subject subject,                           CallbackHandler callbackHandler,                           Map<java.lang.String, ?> sharedState,                           Map<java.lang.String, ?> options) {        this.subject = subject;        this.callbackHandler = callbackHandler;    }    /**     * Authenticate the user by prompting for a user name and password.     */    public boolean login() throws LoginException {        // prompt for a user name and password        if (callbackHandler == null)            throw new LoginException("Error: no CallbackHandler available " +                    "to garner authentication information from the user");        Callback[] callbacks = new Callback[4];        callbacks[0] = new NameCallback("user name");        callbacks[1] = new PasswordCallback("password", false);        callbacks[2] = new TextOutputCallback(TextOutputCallback.INFORMATION, "hello, just a msg!");        callbacks[3] = new TextOutputCallback(TextOutputCallback.WARNING, "just warn you!");        try {            callbackHandler.handle(callbacks);            NameCallback nameCallback = (NameCallback) callbacks[0];            PasswordCallback passwordCallback = (PasswordCallback) callbacks[1];            username = nameCallback.getName();            char[] tmpPassword = passwordCallback.getPassword();            passwordCallback.clearPassword();// clean password in memory space            if (tmpPassword == null) {                tmpPassword = new char[0];// treat a NULL password as an empty password            }            password = new char[tmpPassword.length];            System.arraycopy(tmpPassword, 0, password, 0, tmpPassword.length);        } catch (Exception e) {            e.printStackTrace();        }        // verify the username/password        boolean usernameCorrect = false;        if (username.equals("user")) usernameCorrect = true;        if (usernameCorrect &&                password.length == 3 &&                password[0] == 'p' &&                password[1] == 'w' &&                password[2] == 'd') {            userPwdSucceeded = true;        } else {            userPwdSucceeded = false;            cleanUserAndPwdData();            if (!usernameCorrect) {                throw new FailedLoginException("User Name Incorrect");            } else {                throw new FailedLoginException("Password Incorrect");            }        }        return userPwdSucceeded;    }    public boolean commit() throws LoginException {        if (!userPwdSucceeded) return false;        // add a Principal (authenticated identity) to the Subject        userPrincipal = new SamplePrincipal(username);        subject.getPrincipals().add(userPrincipal);        // in any case, clean out state        cleanUserAndPwdData();        return commitSucceeded = true;    }    public boolean abort() throws LoginException {        if (!userPwdSucceeded) return false;        if (commitSucceeded) {            logout();        } else {            cleanState();        }        return true;    }    public boolean logout() throws LoginException {        subject.getPrincipals().remove(userPrincipal);        cleanState();        userPwdSucceeded = commitSucceeded;        return true;    }    private void cleanState() {        userPwdSucceeded = false;        cleanUserAndPwdData();        userPrincipal = null;    }    private void cleanUserAndPwdData() {        username = null;        if (password != null) {            for (int i = 0; i < password.length; i++)                password[i] = ' ';            password = null;        }    }}


test_jaas.config 登录配置文件

/** 登录配置 **/Sample {   jaas.MyLoginModule required debug=true;};

Principal 用户身份信息

示例代码:

package jaas;import java.security.Principal;public class SamplePrincipal implements Principal {    private String name;    public SamplePrincipal(String name) {        this.name = name;    }    public String getName() {        return name;    }    public boolean equals(Object o) {        if (o == null)            return false;        if (this == o)            return true;        if (!(o instanceof SamplePrincipal))            return false;        SamplePrincipal that = (SamplePrincipal) o;        if (this.getName().equals(that.getName()))            return true;        return false;    }    public int hashCode() {        return name.hashCode();    }}


CallbackHandler 用户交互回调接口

用户代码通过该回调可传入用户名、密码 或其它信息 供 验证服务器使用,java 预置了多种回调实现:

 * @see javax.security.auth.callback.ChoiceCallback * @see javax.security.auth.callback.ConfirmationCallback * @see javax.security.auth.callback.LanguageCallback * @see javax.security.auth.callback.NameCallback * @see javax.security.auth.callback.PasswordCallback * @see javax.security.auth.callback.TextInputCallback * @see javax.security.auth.callback.TextOutputCallback
示例代码:

package jaas;import javax.security.auth.callback.*;import java.io.IOException;public class MyCallbackHandler implements CallbackHandler {    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {        for (int i = 0; i < callbacks.length; i++) {            if (callbacks[i] instanceof TextOutputCallback) {// display the message according to the specified type                TextOutputCallback toc = (TextOutputCallback) callbacks[i];                switch (toc.getMessageType()) {                    case TextOutputCallback.INFORMATION:                        System.out.println(toc.getMessage());                        break;                    case TextOutputCallback.ERROR:                        System.err.println("ERROR: " + toc.getMessage());                        break;                    case TextOutputCallback.WARNING:                        System.err.println("WARNING: " + toc.getMessage());                        break;                    default:                        throw new IOException("Unsupported message type: " + toc.getMessageType());                }            } else if (callbacks[i] instanceof NameCallback) {// prompt the user for a username                NameCallback nc = (NameCallback) callbacks[i];                String name = "user";// TODO 这里可以实现为从控制台允许用户输入等方式接收用户参数。。。                nc.setName(name);            } else if (callbacks[i] instanceof PasswordCallback) {// prompt the user for sensitive information                PasswordCallback pc = (PasswordCallback) callbacks[i];                String pwd = "pwd";// TODO 这里可以实现为从控制台允许用户输入等方式接收用户参数。。。                pc.setPassword(pwd.toCharArray());            } else {                throw new UnsupportedCallbackException                        (callbacks[i], "Unrecognized Callback");            }        }    }}

测试

当登录模块和用户回调实现好以后,就可以进行测试,示例代码:

package jaas;import javax.security.auth.Subject;import javax.security.auth.login.LoginContext;import javax.security.auth.login.LoginException;public class Main {    public static void main(String[] args) throws LoginException {        // 配置文件中查找 Sample 名字的 LoginModule,并指定 CallbackHandler        LoginContext lc = new LoginContext("Sample", new MyCallbackHandler());        try {            lc.login();            Subject subject = lc.getSubject();//            System.out.println(subject);            System.out.println("Authentication succeeded!");        } catch (LoginException le) {            System.err.println("Authentication failed:" + le.getMessage());        }    }}

注意,上述的 new LoginContext("Sample", new MyCallbackHandler()); 是将 登录模块和用户回调关联起来,Sample为登录配置文件中指定的名字,java的jaas服务会通过 -D参数读取并初始化该登录模块,回调接口为用户自定义的交互对象,运行参数如下:

-Djava.security.auth.login.config=/xx/test/test_jaas.config

运行结果

由于示例代码中在 MyCallbackHandler 中硬编码指定了正确的用户名和密码,所以可以看到正确的输出:“Authentication succeeded!”

MyCallbackHandler 为自定义的交互类,可以实现为通过 web 请求中获取用户名和密码,或者从控制台 System.in  接收输入的用户名和密码等。

原创粉丝点击