基于请求参数的Spring安全认证

18

我正在处理的应用程序已经使用Spring Security来处理基于表单的认证。现在的要求是,如果在请求参数中找到了一个令牌,则需要通过外部服务以编程方式登录用户if

换句话说,如果存在一个特定的请求参数,比如"token",它需要使用该令牌调用外部服务来验证它是否是有效的令牌。如果是,则将登录用户。

我无法弄清楚如何在哪里"触发"或"挂钩"Spring Security来检查此参数并进行验证,然后在适当时对用户进行身份验证,因为没有登录表单。我认为在Spring Security中应该有一些可以扩展或自定义来完成这个工作的东西?

我查看了Spring Security文档,并想知道AbstractPreAuthenticatedProcessingFilter是否是开始的正确方式?

2个回答

25

我在我的应用程序中有一个类似的设置。据我所知,以下是基本要素:

您需要创建一个AuthenticationProvider

public class TokenAuthenticationProvider implements AuthenticationProvider {

    @Autowired private SomeService userSvc;

    @Override
    public Authentication authenticate(Authentication auth) throws AuthenticationException {
        if (auth.isAuthenticated())
            return auth;

        String token = auth.getCredentials().toString();
        User user = userSvc.validateApiAuthenticationToken(token);
        if (user != null) {
            auth = new PreAuthenticatedAuthenticationToken(user, token);
            auth.setAuthenticated(true);
            logger.debug("Token authentication. Token: " + token + "; user: " + user.getDisplayName());
        } else
            throw new BadCredentialsException("Invalid token " + token);
        return auth;
    }
}
你还需要创建一个 Filter 将自定义参数转换为身份验证令牌:
public class AuthenticationTokenFilter implements Filter {


    @Override
    public void init(FilterConfig fc) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain fc) throws IOException, ServletException {
        SecurityContext context = SecurityContextHolder.getContext();
        if (context.getAuthentication() != null && context.getAuthentication().isAuthenticated()) {
            // do nothing
        } else {
            Map<String,String[]> params = req.getParameterMap();
            if (!params.isEmpty() && params.containsKey("auth_token")) {
                String token = params.get("auth_token")[0];
                if (token != null) {
                    Authentication auth = new TokenAuthentication(token);
                    SecurityContextHolder.getContext().setAuthentication(auth);
                }
            }
        }

        fc.doFilter(req, res);
    }

    @Override
    public void destroy() {

    }

    class TokenAuthentication implements Authentication {
        private String token;
        private TokenAuthentication(String token) {
            this.token = token;
        }
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return new ArrayList<GrantedAuthority>(0);
        }
        @Override
        public Object getCredentials() {
            return token;
        }
        @Override
        public Object getDetails() {
            return null;
        }
        @Override
        public Object getPrincipal() {
            return null;
        }
        @Override
        public boolean isAuthenticated() {
            return false;
        }
        @Override
        public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        }
        @Override
        public String getName() {
            // your custom logic here
        }
    }

 }
您需要为这些创建豆子:
<beans:bean id="authTokenFilter" class="com.example.security.AuthenticationTokenFilter" scope="singleton" />
<beans:bean id="tokenAuthProvider" class="com.example.security.TokenAuthenticationProvider" />

最后,您需要将这些Bean导入您的安全配置中(根据需要进行调整):

<sec:http >
   <!-- other configs here -->
   <sec:custom-filter ref="authTokenFilter" after="BASIC_AUTH_FILTER" /> <!-- or other appropriate filter -->
</sec:http>

<sec:authentication-manager>
    <!-- other configs here -->
    <sec:authentication-provider ref="tokenAuthProvider" />
</sec:authentication-manager>

可能有其他方法,但这个肯定有效(目前使用Spring Security 3.1)。


谢谢提供代码示例。但是我无法使其工作,因为自定义的tokenAuthProvider没有被调用。有什么建议吗? - Bing Qiao
4
这是因为你的过滤器需要对认证管理器进行引用,在那里你会调用authenticationmanager.authenticate(authToken)。 - El Guapo
你如何处理失败?我已经使用Java配置实现了你的解决方案,但我看不到处理BadCredentialsException的方法。因此,客户端收到一个丑陋的500内部错误,而不是未经授权的401。 - ampofila
1
@ampofila 啊,我忘记了那部分。您还需要实现一个AuthenticationFailureHandler,它可以根据异常具有适当的逻辑。(说实话,在一般情况下,您实际上不需要显式地抛出BadCredentialsException,但由于某些其他原因,我们在此应用程序中需要它) - Dan

5
如果您使用Spring MVC控制器或服务,并且目标请求参数被传递,则可以使用@PreAuthorize Spring安全注释。例如,如果您有一些Spring服务可以检查传递的令牌并在通过验证的情况下执行身份验证:
@Service("authenticator")
class Authenticator {        
...
public boolean checkTokenAndAuthenticate(Object token) {
    ...
    //check token and if it is invalid return "false"
    ...
    //if token is valid then perform programmatically authentication and return "true"  
}
...             
}    

然后,使用Spring Security的@PreAuthorize注释,您可以按照以下方式完成此操作:

...
@PreAuthorize("@authenticator.checkTokenAndAuthenticate(#token)")
public Object methodToBeChecked(Object token) { ... }
...

此外,您应该通过添加 spring-security-aspects 到 POM(或将 jar 添加到 classpath)并通过 <global-method-security> 启用 Spring 安全注释。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接