17

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

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

我无法弄清楚如何以及在何处“触发”或“连接到”Spring Security 以检查此参数并进行验证,然后在适当的时候对用户进行身份验证,因为没有登录表单。我认为 Spring Security 中应该有一些可以扩展或定制的东西来做到这一点?

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

4

2 回答 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
        }
    }

 }

您需要为这些创建 bean:

<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)。

于 2013-11-05T15:32:08.380 回答
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 安全注释并将 spring-security-aspects 添加到 POM(或 jar 到类路径)。

于 2013-11-08T11:37:03.157 回答