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;
};
我正在使用 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;
};