Java 预验证并将用户名转发给默认验证提供程序
我是Spring Security的新手,如果这是直截了当的话,请原谅我 我们正在重写遗留web应用程序的UI层。我们决定新的UI层将基于SpringMVC,使用SpringSecurity来处理安全性和授权 我一直在考虑在新应用程序中设置安全性,以模仿我们在以前的应用程序中所做的。在旧应用程序中,我们基本上有两个用户入口点:Java 预验证并将用户名转发给默认验证提供程序,java,spring,authentication,spring-mvc,spring-security,Java,Spring,Authentication,Spring Mvc,Spring Security,我是Spring Security的新手,如果这是直截了当的话,请原谅我 我们正在重写遗留web应用程序的UI层。我们决定新的UI层将基于SpringMVC,使用SpringSecurity来处理安全性和授权 我一直在考虑在新应用程序中设置安全性,以模仿我们在以前的应用程序中所做的。在旧应用程序中,我们基本上有两个用户入口点: 内部用户通过HTTP基本身份验证进行身份验证 使用客户端LDAP服务器执行实际身份验证。 这种身份验证机制是在JBoss服务器上配置的,它也是 集装箱管理 外部用户通过
- 内部用户通过HTTP基本身份验证进行身份验证 使用客户端LDAP服务器执行实际身份验证。 这种身份验证机制是在JBoss服务器上配置的,它也是 集装箱管理
- 外部用户通过验证凭据的第三方身份验证服务登录。外部用户角色存储在LDAP服务器中。当第三方身份验证服务对凭据进行身份验证时,将使用用户名和硬编码密码在JBoss配置的安全域上对凭据进行身份验证,以便加载它们的角色李>
AbstractPreAuthenticatedProcessingFilter
并通过它提供凭据,但它似乎确实将凭据正确地转发给“默认”身份验证提供程序
<http>
...
<http-basic />
<custom-filter position="PRE_AUTH_FILTER" ref="ExternalLoginFilter" />
</http>
<beans:bean id="ExternalLoginFilter" class="com.foo.ExternalLoginPreAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="internal-user" password="password" authorities="ROLE_USER, ROLE_INTERNAL" />
<user name="external-user" password="password" authorities="ROLE_USER, ROLE_EXTERNAL" />
</user-service>
</authentication-provider>
</authentication-manager>`
}
我还试着像一些示例建议的那样设置“预验证身份验证提供者”,但这似乎期望我的ExternalLoginPreAuthenticationFilter
也解决了用户角色问题
我如何配置SpringMVC以允许上面的场景?基本上,我需要能够告诉Spring Security以最少的侵入方式,使用特定用户名/密码对默认身份验证提供者执行登录,最好不要有太多的黑客(旧应用程序使用)
溶液说明:拉尔夫的溶液似乎有效,特别是这一部分:
我认为应该使用PreAuthenticatedAuthenticationProvider并将preAuthenticatedUserDetailsService变量设置为内存中的AuthenticationUserDetailsService
然而,CSRF保护似乎也很糟糕。当我登录并被重定向到主页时,来自此页面的任何HTTP帖子都将无法通过CSRF检查。在发布之前对主页的后续获取修复了该问题,因此Spring似乎以某种方式不正确地覆盖了当前的CSRF令牌。我发现了一个解决问题的方法。即使它声称是固定的,我也无法解决它。虽然bug报告链接到论坛中的一个变通方法,但我已经习惯了下面的变通方法,这似乎是可行的
诀窍是将AuthenticationManager
注入控制器
,然后自己登录:
@Controller
@RequestMapping(value = "/external-login")
public class ExternalLoginController {
private AuthenticationManager authenticationManager;
@Autowired
public ExternalLoginController(AuthenticationManager authenticationManager){
this.authenticationManager = authenticationManager;
}
// ...
@RequestMapping(method = RequestMethod.POST)
public String login(){
// Do this after third-party authentication service accepts credentials
String username = "external-user"; // or whatever username was authenticated by third-party
UsernamePasswordAuthenticationToken credentials = new UsernamePasswordAuthenticationToken(username, "password");
Authentication auth = authenticationManager.authenticate(credentials);
SecurityContextHolder.getContext().setAuthentication(auth);
return "redirect:/";
}
}
问题是,
AuthenticationProvider
在AuthenticationManager
中由提供的身份验证凭据类(通常是AbstractAuthenticationToken
的子类)选择
您的PreAuthenticationProcessingFilter
将创建一个preauthenticationdauthenticationtoken
,该令牌通常由preauthenticationdauthenticationprovider
使用
因此,要么:
- 您注册了一个
,该提供者“使用”了AuthenticationProvider
令牌,并执行您想要的操作,或者preauthenticationdauthenticationprovider
- 更改
以创建其他类型的令牌(例如PreAuthenticationProcessingFilter
,该令牌由您使用的“普通”身份验证提供程序“消费”(AbstractUserDetailsAuthenticationProvider的子类))UsernamePasswordAuthenticationToken
我认为应该使用
PreAuthenticatedAuthenticationProvider
并将preAuthenticatedUserDetailsService
变量设置为内存中的AuthenticationUserDetailsService
。您是否在AbstractPreAuthenticatedProcessingFilter.doAuthenticate中设置断点以检查此方法是否正确调用了吗?我尝试了您的上一个建议,它似乎有效(使用UserDetailsByNameServiceWrapper作为UserDetailsService)。然而,当我使用这种方法登录时,CSRF保护似乎崩溃了。后续的表单帖子会给我一个带有CSRF失败错误消息的http 403?我尝试将AuthenticationManager注入到处理外部登录的控制器中,如果我直接调用它并随后在SecurityContextHolder上设置返回的身份验证,那么CSRF和登录将按预期工作。有什么想法吗?
@Controller
@RequestMapping(value = "/external-login")
public class ExternalLoginController {
private AuthenticationManager authenticationManager;
@Autowired
public ExternalLoginController(AuthenticationManager authenticationManager){
this.authenticationManager = authenticationManager;
}
// ...
@RequestMapping(method = RequestMethod.POST)
public String login(){
// Do this after third-party authentication service accepts credentials
String username = "external-user"; // or whatever username was authenticated by third-party
UsernamePasswordAuthenticationToken credentials = new UsernamePasswordAuthenticationToken(username, "password");
Authentication auth = authenticationManager.authenticate(credentials);
SecurityContextHolder.getContext().setAuthentication(auth);
return "redirect:/";
}
}