Spring 具有数据库授权的 JAAS 身份验证

Spring JAAS Authentication with database authorization

我正在使用 Spring 安全 4.0。我的登录模块是在应用程序服务器中配置的,所以我必须使用 JAAS 进行身份验证,但我的用户详细信息存储在数据库中,因此一旦经过身份验证的用户对象将通过查询数据库创建。您能否告诉我如何实现这一点,即 LDAP 身份验证和从数据库加载用户详细信息。还有如何使用eh-cache缓存用户对象,让用户对象在service/dao层可以访问。

这可以使用 CustomAuthentication Provider 来实现。下面是代码。

import java.util.Arrays;
import java.util.List;

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authentication.jaas.JaasGrantedAuthority;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.sun.security.auth.UserPrincipal;

public class CustomAutenticationProvider extends DaoAuthenticationProvider implements AuthenticationProvider {
    private AuthenticationProvider delegate;

    public CustomAutenticationProvider(AuthenticationProvider delegate) {
        this.delegate = delegate;
    }

    @Override
    public Authentication authenticate(Authentication authentication) {
        Authentication a = delegate.authenticate(authentication);

        if(a.isAuthenticated()){
            a = super.authenticate(a);
        }else{
            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }

        return a;
    }

    private List<GrantedAuthority> loadRolesFromDatabaseHere(String name) {
        GrantedAuthority grantedAuthority =new JaasGrantedAuthority(name, new UserPrincipal(name));
        return Arrays.asList(grantedAuthority);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return delegate.supports(authentication);
    }

    /* (non-Javadoc)
     * @see org.springframework.security.authentication.dao.DaoAuthenticationProvider#additionalAuthenticationChecks(org.springframework.security.core.userdetails.UserDetails, org.springframework.security.authentication.UsernamePasswordAuthenticationToken)
     */
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails,
            UsernamePasswordAuthenticationToken authentication)
                    throws AuthenticationException {


        if(!authentication.isAuthenticated())
            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));


    }
}

DAOAuthentication 所需的用户详细信息

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import com.testjaas.model.User;
import com.testjaas.model.UserRepositoryUserDetails;


@Component
public class AuthUserDetailsService implements UserDetailsService {


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("loadUserByUsername called !!");
        com.testjaas.model.User user = new User();
        user.setName(username);
        user.setUserRole("ROLE_ADMINISTRATOR");
        if(null == user) {
            throw new UsernameNotFoundException("User " + username + " not found.");
        }

        return new UserRepositoryUserDetails(user);
    }


}

RoleGrantor - 这将是 class JAAS 身份验证 Spring 所需的虚拟

import java.security.Principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

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

public class RoleGranterFromMap implements AuthorityGranter {

    private static Map<String, String> USER_ROLES = new HashMap<String, String>();

    static {
        USER_ROLES.put("test", "ROLE_ADMINISTRATOR");
        //USER_ROLES.put("test", "TRUE");
    }

    public Set<String> grant(Principal principal) {
        return Collections.singleton("DUMMY");
    }
}

SampleLogin - 这应该替换为您的登录模块

import java.io.Serializable;
import java.security.Principal;
import java.util.HashMap;
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.login.LoginException;
import javax.security.auth.spi.LoginModule;

public class SampleLoginModule implements LoginModule {

    private Subject subject;
    private String password;
    private String username;
    private static Map<String, String> USER_PASSWORDS = new HashMap<String, String>();

    static {
        USER_PASSWORDS.put("test", "test");
    }

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

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

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

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

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

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

    public boolean login() throws LoginException {

        if (USER_PASSWORDS.get(username) == null
                || !USER_PASSWORDS.get(username).equals(password)) {
            throw new LoginException("username is not equal to password");
        }

        subject.getPrincipals().add(new CustomPrincipal(username));
        return true;
    }

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

    private static class CustomPrincipal implements Principal, Serializable {
        private final String username;

        public CustomPrincipal(String username) {
            this.username = username;
        }

        public String getName() {
            return username;
        }
    }

}

Spring XML 配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:security="http://www.springframework.org/schema/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
    <security:http auto-config="true">
        <security:intercept-url pattern="/*" access="isAuthenticated()"/>
    </security:http>
    <!-- <security:authentication-manager>
        <security:authentication-provider ref="jaasAuthProvider" />
    </security:authentication-manager> -->

    <bean id="userDetailsService" class="com.testjaas.service.AuthUserDetailsService"></bean>
    <bean id="testService" class="com.testjaas.service.TestService"/>
    <bean id="applicationContextProvider" class="com.testjaas.util.ApplicationContextProvider"></bean>

    <security:authentication-manager>
        <security:authentication-provider ref="customauthProvider"/>
    </security:authentication-manager>

    <bean id="customauthProvider" class="com.testjaas.security.CustomAutenticationProvider">
        <constructor-arg name="delegate" ref="jaasAuthProvider" />
        <property name="userDetailsService" ref="userDetailsService" />
    </bean>

    <bean id="jaasAuthProvider" class="org.springframework.security.authentication.jaas.JaasAuthenticationProvider">
        <property name="loginConfig" value="classpath:pss_jaas.config" />
        <property name="authorityGranters">
            <list>
                <bean class="com.testjaas.security.RoleGranterFromMap" />
            </list>
        </property>
        <property name="loginContextName" value="JASSAuth" />
        <property name="callbackHandlers">
            <list>
                <bean class="org.springframework.security.authentication.jaas.JaasNameCallbackHandler" />
                <bean class="org.springframework.security.authentication.jaas.JaasPasswordCallbackHandler" />
            </list>
        </property>
    </bean>
</beans>

Jaas 配置示例

JASSAuth {
  com.testjaas.security.SampleLoginModule required;
};