预验证并转发用户名到默认身份验证提供程序

时间:2014-01-31 06:51:15

标签: java spring authentication spring-mvc spring-security

我是Spring Security的新手,请原谅我,如果这是直截了当的话。

我们正在重写旧版网络应用的UI层。我们决定新的UI层将基于Spring MVC和Spring Security来处理安全性和授权。

我一直在寻找在新应用中设置安全性来模仿我们在之前的应用中所拥有的内容。在旧的应用程序中,我们基本上有两个用户入口点:

  • 内部用户通过HTTP基本身份验证进行身份验证 使用客户端LDAP服务器执行实际身份验证。 此身份验证机制在JBoss服务器上配置,同样如此 容器管理。
  • 外部用户通过验证凭据的第三方身份验证服务登录。外部用户角色存储在LDAP服务器中。当第三方身份验证服务对凭据进行身份验证时,将使用用户名和硬编码密码在JBoss配置的安全域上对其进行身份验证,以便加载其角色。

我想我会尝试在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并将preAuthenticatedUserDetailsS​​ervice变量设置为内存中的AuthenticationUserDetailsS​​ervice应该是可行的方法。

然而,它似乎在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:/";
    }
}

1 个答案:

答案 0 :(得分:1)

问题是,AuthenticationProvider是由提供的身份验证凭据类(通常是AuthenticationManager的子类)在AbstractAuthenticationToken中选择的。

PreAuthenticationProcessingFilter PreAuthenticatedAuthenticationToken会创建PreAuthenticatedAuthenticationProviderAuthenticationProvider正常“消费”。{/ 1>

所以:

  • 您注册PreAuthenticatedAuthenticationProvider“消费”PreAuthenticationProcessingFilter令牌,然后执行您想要的操作,或者
  • 您更改UsernamePasswordAuthenticationToken以创建其他类型的令牌(例如,由您使用的“普通”身份验证提供程序“消耗”的AbstractUserDetailsAuthenticationProviderPreAuthenticatedAuthenticationProvider的子类) )

我认为使用preAuthenticatedUserDetailsService并将AuthenticationUserDetailsService变量设置为内存{{1}}应该是可行的方法。