我是Spring Security的新手,请原谅我,如果这是直截了当的话。
我们正在重写旧版网络应用的UI层。我们决定新的UI层将基于Spring MVC和Spring Security来处理安全性和授权。
我一直在寻找在新应用中设置安全性来模仿我们在之前的应用中所拥有的内容。在旧的应用程序中,我们基本上有两个用户入口点:
我想我会尝试在Spring Security中模仿这个功能,但到目前为止还是很短。我在测试中使用内存中的身份验证提供程序代替LDAP,因为它更容易。我已经为内部用户提供了http基本身份验证。
我已尝试继承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:
public class ExternalLoginPreAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {
@Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest req) {
return "password";
}
@Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest req) {
HttpSession session = req.getSession(false);
if(session != null){
return session.getAttribute("app.external-user.username");
}
return null;
}
}
我也尝试过设置一个“preAuthenticatedAuthenticationProvider”,就像一些例子一样,但是我的ExternalLoginPreAuthenticationFilter
似乎也已经解决了用户角色。
我是如何配置Spring MVC以允许上述场景的?基本上,我需要能够告诉Spring Security以最少侵入的方式使用特定的用户名/密码对默认身份验证提供程序执行登录,最好没有太多hacks(旧应用程序使用)。
关于解决方案的说明:虽然Ralph的解决方案似乎有效,但特别是这一部分:
我认为使用PreAuthenticatedAuthenticationProvider并将preAuthenticatedUserDetailsService变量设置为内存中的AuthenticationUserDetailsService应该是可行的方法。
然而,它似乎在CSRF保护方面也很糟糕。当我登录并重定向到主页时,此页面中的任何HTTP POST都将无法通过CSRF检查。在POST之前的主页的后续GET修复了问题,因此Spring似乎以某种方式不正确地覆盖了当前的CSRF令牌。我发现了bug report详细说明了这个问题。即使它声称是固定的,我也无法解决它。虽然错误报告链接到论坛中的变通方法,但我已经习惯了以下解决方法,这似乎有效。
诀窍是将AuthenticationManager
注入Controller
并自行登录:
@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:/";
}
}
答案 0 :(得分:1)
问题是,AuthenticationProvider
是由提供的身份验证凭据类(通常是AuthenticationManager
的子类)在AbstractAuthenticationToken
中选择的。
PreAuthenticationProcessingFilter
PreAuthenticatedAuthenticationToken
会创建PreAuthenticatedAuthenticationProvider
,AuthenticationProvider
正常“消费”。{/ 1>
所以:
PreAuthenticatedAuthenticationProvider
“消费”PreAuthenticationProcessingFilter
令牌,然后执行您想要的操作,或者UsernamePasswordAuthenticationToken
以创建其他类型的令牌(例如,由您使用的“普通”身份验证提供程序“消耗”的AbstractUserDetailsAuthenticationProvider
(PreAuthenticatedAuthenticationProvider
的子类) )我认为使用preAuthenticatedUserDetailsService
并将AuthenticationUserDetailsService
变量设置为内存{{1}}应该是可行的方法。