第 27 章 使用JAAS机制

可以在Spring Security中使用JAAS机制进行用户的身份认证。

JAAS即Java Authentication and Authorization Service,它是JDK自带的一套专门用于处理用户认证和授权的标准API,Spring Security中可以使用API作为AuthenticationProvider处理用户认证与授权。

配置文件中,我们使用JaasAuthenticationProvider作为AuthenticationProvider。

<http>
	<intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
	<intercept-url pattern="/**" access="ROLE_USER" />
	<form-login/>
	<logout/>
</http>

<authentication-manager>
	<authentication-provider ref="jaasAuthenticationProvider"/>
</authentication-manager>

<beans:bean id="jaasAuthenticationProvider"
	class="org.springframework.security.authentication.jaas.JaasAuthenticationProvider">
	<beans:property name="loginConfig" value="/WEB-INF/login.conf" />
	<beans:property name="loginContextName" value="JAASTest" />
	<beans:property name="callbackHandlers">
		<beans:list>
			<beans:bean class="org.springframework.security.authentication.jaas.JaasNameCallbackHandler" />
			<beans:bean class="org.springframework.security.authentication.jaas.JaasPasswordCallbackHandler" />
		</beans:list>
	</beans:property>
	<beans:property name="authorityGranters">
		<beans:list>
			<beans:bean class="com.family168.springsecuritybook.ch117.AuthorityGranterImpl" />
		</beans:list>
	</beans:property>
</beans:bean>
    

注意不能在http标签中使用auto-config="true"或是在http标签中包含rememberMe,因为rememberMe需要引用userDetailsService,而在使用JaasAuthenticationProvider时,用户数据校验是交由LoginModule处理的,不会使用userDetailsService,所以rememberMe会抛出异常。

我们将JAAS所需的配置文件放在/WEB-INF/login.config。

JAASTest {
    com.family168.springsecuritybook.ch117.LoginModuleImpl required;
};
    

并在配置文件中指明使用JAASTest作为登陆上下文。

<beans:property name="loginContextName" value="JAASTest" />
    

现在要创建LoginModuleImpl用来处理用户登录。


package com.family168.springsecuritybook.ch117;

import java.security.Principal;

import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextInputCallback;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;


public class LoginModuleImpl implements LoginModule {
    private String password;
    private String user;
    private Subject subject;

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

    public boolean commit() throws LoginException {
        return true;
    }

    public void initialize(Subject subject,
        CallbackHandler callbackHandler, Map sharedState, Map options) {
        this.subject = subject;

        try {
            TextInputCallback textCallback = new TextInputCallback(
                    "prompt");
            NameCallback nameCallback = new NameCallback("prompt");
            PasswordCallback passwordCallback = new PasswordCallback("prompt",
                    false);

            callbackHandler.handle(new Callback[] {
                    textCallback, nameCallback, passwordCallback
                });

            password = new String(passwordCallback.getPassword());
            user = nameCallback.getName();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public boolean login() throws LoginException {
        if (!user.equals("user")) {
            throw new LoginException("Bad User");
        }

        if (!password.equals("user")) {
            throw new LoginException("Bad Password");
        }

        subject.getPrincipals().add(new Principal() {
                public String getName() {
                    return "TEST_PRINCIPAL";
                }
            });

        subject.getPrincipals().add(new Principal() {
                public String getName() {
                    return "NULL_PRINCIPAL";
                }
            });

        return true;
    }

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

当用户登录成功时,会通过authorityGranters为权限主体授权,这一步也要自己实现AuthorityGranter接口。


package com.family168.springsecuritybook.ch117;

import java.security.Principal;

import java.util.HashSet;
import java.util.Set;

import org.springframework.security.authentication.jaas.AuthorityGranter;


public class AuthorityGranterImpl implements AuthorityGranter {
    public Set grant(Principal principal) {
        Set rtnSet = new HashSet();

        if (principal.getName().equals("TEST_PRINCIPAL")) {
            rtnSet.add("ROLE_USER");
            rtnSet.add("ROLE_ADMIN");
        }

        return rtnSet;
    }
}
    

至此,JAAS与Spring Security结合进行认证授权的功能已经完成,每一步都要件功能写死在代码里,让人感觉很不舒服。

实例在ch117。