具有数据库授权的 Spring JAAS 身份验证
Posted
技术标签:
【中文标题】具有数据库授权的 Spring JAAS 身份验证【英文标题】:Spring JAAS Authentication with database authorization 【发布时间】:2015-10-12 18:07:24 【问题描述】:我正在使用 Spring 安全 4.0。我的登录模块是在应用程序服务器中配置的,所以我必须使用 JAAS 进行身份验证,但我的用户详细信息存储在数据库中,因此一旦通过身份验证的用户对象将通过查询数据库创建。您能否让我知道如何实现这一点,即 LDAP 身份验证并从数据库加载用户详细信息。还有如何使用eh-cache缓存用户对象,以便在service/dao层访问用户对象。
【问题讨论】:
【参考方案1】:这可以使用 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 - 这将是 Spring JAAS 身份验证所需的虚拟类
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 JAAS 身份验证的主要内容,如果未能解决你的问题,请参考以下文章
绕过对用户名/密码的 JConsole 要求 - 当使用带有 JMX 的 Jaas 自定义登录模块来处理授权和身份验证时
将 JAAS 用于具有 Spring 安全性的 LDAP 密码
与 Spring Security/Apache Shiro 相比,JAAS 的缺点是啥?