我对弹簧安全性有点问题:)
我的目标是什么: 使用自定义角色配置LDAP auth,从数据库中获取,并记住我的功能。
做了什么:
我的问题是: '记住我'工作正常,persistent_logins表创建成功,它存储令牌很好。但是当用户返回网站时,春季节目未被授权'页。
我认为这是因为'记住我'对我的自定义角色一无所知,并从LDAP中获取角色。
问题是:如何告诉'记住我'通过我的 CustomLdapAuthoritiesPopulator 获取角色?
我的applicationContext.xml
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://ldap.forumsys.com:389"/>
<property name="userDn" value="cn=read-only-admin,dc=example,dc=com"/>
<property name="password" value="password"/>
</bean>
<bean name="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg ref="contextSource"/>
<property name="userDnPatterns">
<list>
<value>uid={0},dc=example,dc=com</value>
</list>
</property>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="my.extra.CustomLdapAuthoritiesPopulator"/>
</constructor-arg>
</bean>
<bean id="tokenRepository"
class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
<property name="createTableOnStartup" value="false"/>
<property name="dataSource" ref="myDataSource"/>
</bean>
<security:authentication-manager>
<security:authentication-provider ref="ldapAuthProvider"/>
</security:authentication-manager>
<security:http auto-config="true" use-expressions="true">
<security:access-denied-handler error-page="/403"/>
<security:intercept-url pattern="/login*" access="permitAll()"/>
<security:intercept-url pattern="/favicon.ico" access="permitAll()"/>
<security:intercept-url pattern="/resources/**" access="permitAll()"/>
<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
<security:form-login login-page='/login' login-processing-url="/j_spring_security_check"
default-target-url="/" authentication-failure-url="/login?fail"/>
<security:remember-me key="_spring_security_remember_me" token-validity-seconds="14400"
token-repository-ref="tokenRepository"
user-service-ref="ldapUserService"/>
</security:http>
<security:ldap-user-service id="ldapUserService" server-ref="contextSource"
group-search-base="dc=example,dc=com"
group-search-filter="ou={0})"
user-search-base="dc=example,dc=com"
user-search-filter="uid={0}"/>
在调试期间,当用户返回时,不调用CustomLdapAuthoritiesPopulator。 我添加了代码来检查用户的角色(在欢迎页面和自定义403页面上)。
Collection<? extends GrantedAuthority> roles = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
roles.forEach(System.out::println);
用户登录后,欢迎页面显示&#34; ROLE_USER&#34;,&#34; ROLE_ADMIN&#34;
用户返回后,403页显示&#34;&#34 ;; (无)
答案 0 :(得分:1)
如果您使用的身份验证提供程序不使用
UserDetailsService
(例如, LDAP提供程序),那么除非您还拥有应用程序上下文中的UserDetailsService bean 。
在applicationContext.xml
中尝试添加: -
添加RoleVoter。来自Spring Docs
投票,如果任何
ConfigAttribute.getAttribute()
以前缀表示它是一个角色。默认前缀字符串为ROLE_
,但可以覆盖任何值。它也可以设置为空,这意味着基本上任何属性都将被投票。如下面进一步描述的,空前缀的效果可能不太理想。如果没有配置属性以角色前缀开头,则弃权。如果从角色前缀开始与
GrantedAuthority
完全匹配ConfigAttribute
,则表示授予访问权限。如果与角色前缀不一致的ConfigAttribute没有完全匹配的GrantedAuthority,则表示拒绝访问。所有比较和前缀都区分大小写。
<bean id="roleVoter"
class="org.springframework.security.access.vote.RoleVoter" p:rolePrefix="" />
添加AuthenticatedVoter。来自Soring Docs
如果
ConfigAttribute.getAttribute()
或IS_AUTHENTICATED_FULLY
或IS_AUTHENTICATED_REMEMBERED
存在IS_AUTHENTICATED_ANONYMOUSLY
则投票。此列表按照最严格的检查顺序进行最严格的检查。将检查当前的身份验证以确定主体是否具有特定级别的身份验证。 “FULLY”身份验证选项意味着用户已完全通过身份验证(即
AuthenticationTrustResolver.isAnonymous(Authentication)
为false且AuthenticationTrustResolver.isRememberMe(Authentication)
为false)。如果通过remember-me对身份验证进行身份验证,则"REMEMBERED"
将授予访问权限。如果通过remember-me,匿名或通过完全身份验证对主体进行身份验证,则"ANONYMOUSLY"
将授予访问权限。所有比较和前缀都区分大小写。
<bean id="authVoter"
class="org.springframework.security.access.vote.AuthenticatedVoter">
</bean>
现在,配置一个使用上述两个选民的Spring AccessDecisionManager
,以便确定用户是否被授予访问资源的正确权限。
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.ConsensusBased">
<property name="allowIfAllAbstainDecisions" value="false" />
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
<ref bean="authVoter" />
</list>
</property>
</bean>
有关详细信息:访问Spring security 3 remember-me with LDAP authentication
编辑1:
编辑2:
源代码最初发布于Configuring Spring Security Form Login with Remember-Me Enabled。
创建自定义RememberMeProcessingFilter:
public class MyRememberMeProcessingFilter extends RememberMeProcessingFilter {
private myService;
@Override
protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
// perform some custom logic when the user has been 'remembered' & authenticated - e.g. update a login count etc
this.myService.doSomeCustomBusinessLogic(authResult.getName());
super.onSuccessfulAuthentication(request, response, authResult);
}
}
RememberMeProcessingFilter
包含您希望在用户返回您的网站时运行并由应用程序“记住”的任何自定义业务逻辑。
不要忘记添加:
<custom-authentication-provider />
这确保记住我实际上被用作身份验证提供程序 - 即当您的用户返回先前已被要求记住时,这会将我添加到检查用户的提供商列表中经过验证。
答案 1 :(得分:0)
使用错误的解决方案
<security:ldap-user-service/>
如果您想从数据库应用自定义角色,请记住我&#39;功能。
仍然需要实现自定义UserDetailsService并从remember-me部分引用它。
<security:http>
....
<security:remember-me key="_spring_security_remember_me" token-validity-seconds="14400"
token-repository-ref="tokenRepository"
user-service-ref="rememberMeUserDetailsService"/>
</security:http>
<bean id="rememberMeUserDetailsService" class="gpb.extra.RememberMeUserDetailsService"/>
它有点棘手,但有效,我的数据库存储每个用户名,因为它存储角色。所以,我可以检查没有LDAP的任何用户。
package gpb.extra;
import gpb.database.models.User;
import gpb.database.utils.HibernateUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
public class RememberMeUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Collection<GrantedAuthority> authorities = new HashSet<>();
List<User> users = HibernateUtils.get(User.class, username, "username");
if (users.size() == 0) {
throw new UsernameNotFoundException("User not found");
} else {
User existingUser = users.get(0);
if (!existingUser.getRoles().isEmpty()) {
List<String> roles = Arrays.asList(existingUser.getRoles().split(","));
roles.forEach(t -> authorities.add(new SimpleGrantedAuthority(t.trim())));
}
}
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
return new org.springframework.security.core.userdetails.User(
username, "password", enabled, accountNonExpired,
credentialsNonExpired, accountNonLocked, authorities);
}
}
代码演示了主要观点。
非常感谢@ OO7指出我正确的方式和惊人的帮助:)