整合Spring Security OAuth2和Spring Social

And*_*erk 32 spring-security single-sign-on spring-social spring-boot spring-security-oauth2

我正在使用Spring Boot + Spring Security OAuth2应用程序,我认为该应用程序的灵感来自Dave Syer的示例.应用程序配置为OAuth2授权服务器,单个公共客户端使用资源所有者密码凭据流.成功的令牌配置为JWT.

公共Angular客户端向/ oauth/token发送一个POST请求,其中包含一个包含客户端ID和密码的基本auth头(这是让客户端进行身份验证的最简单方法,即使秘密不是私有的).请求正文包含用户名,密码和授予类型"密码".

除了作为身份验证服务器之外,该应用程序还是用户,团队和组织的RESTful资源服务器.

我正在尝试使用Spring Social添加额外的SSO身份验证流程.我已经将Spring Social配置为通过/ auth/[provider]通过外部提供程序进行身份验证; 但是,以下请求不再正确设置SecurityContext.可能是Spring Security OAuth服务器或客户端覆盖了SecurityContext?

如果我可以在Spring Social流程之后正确设置SecurityContext,我有一个新的TokenGranter,允许新的授权类型"social",它将检查SecurityContextHolder以获得预先认证的用户.

我对SecurityContext的特定问题的解决方案感兴趣(我认为这是Spring OAuth +社交集成的问题),或者是与外部提供程序进行身份验证以及从我们自己的auth服务器获取有效JWT的不同方法.

谢谢!

rba*_*uso 12

我在JHipster生成的Web应用程序上遇到了类似的问题.最后我决定SocialAuthenticationFilter选择Spring Social(通过SpringSocialConfigurer).成功进行社交登录后,服务器会自动生成并返回"自己的"访问令牌,方法是重定向到客户端应用程序.

这是我的尝试:

@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter implements EnvironmentAware {

    //...

    @Inject
    private AuthorizationServerTokenServices authTokenServices;

    @Override
    public void configure(HttpSecurity http) throws Exception {

        SpringSocialConfigurer socialCfg = new SpringSocialConfigurer();
        socialCfg
            .addObjectPostProcessor(new ObjectPostProcessor<SocialAuthenticationFilter>() {
                @SuppressWarnings("unchecked")
                public SocialAuthenticationFilter postProcess(SocialAuthenticationFilter filter){
                    filter.setAuthenticationSuccessHandler(
                            new SocialAuthenticationSuccessHandler(
                                    authTokenServices,
                                    YOUR_APP_CLIENT_ID
                            )
                        );
                    return filter;
                }
            });

        http
            //... lots of other configuration ...
            .apply(socialCfg);
    }        
}
Run Code Online (Sandbox Code Playgroud)

SocialAuthenticationSuccessHandler班级:

public class SocialAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    public static final String REDIRECT_PATH_BASE = "/#/login";
    public static final String FIELD_TOKEN = "access_token";
    public static final String FIELD_EXPIRATION_SECS = "expires_in";

    private final Logger log = LoggerFactory.getLogger(getClass());
    private final AuthorizationServerTokenServices authTokenServices;
    private final String localClientId;

    public SocialAuthenticationSuccessHandler(AuthorizationServerTokenServices authTokenServices, String localClientId){
        this.authTokenServices = authTokenServices;
        this.localClientId = localClientId;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
            HttpServletResponse response, Authentication authentication)
                    throws IOException, ServletException {
        log.debug("Social user authenticated: " + authentication.getPrincipal() + ", generating and sending local auth");
        OAuth2AccessToken oauth2Token = authTokenServices.createAccessToken(convertAuthentication(authentication)); //Automatically checks validity
        String redirectUrl = new StringBuilder(REDIRECT_PATH_BASE)
            .append("?").append(FIELD_TOKEN).append("=")
            .append(encode(oauth2Token.getValue()))
            .append("&").append(FIELD_EXPIRATION_SECS).append("=")
            .append(oauth2Token.getExpiresIn())
            .toString();
        log.debug("Sending redirection to " + redirectUrl);
        response.sendRedirect(redirectUrl);
    }

    private OAuth2Authentication convertAuthentication(Authentication authentication) {
        OAuth2Request request = new OAuth2Request(null, localClientId, null, true, null,
                null, null, null, null);
        return new OAuth2Authentication(request,
                //Other option: new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), "N/A", authorities)
                new PreAuthenticatedAuthenticationToken(authentication.getPrincipal(), "N/A")
                );
    }

    private String encode(String in){
        String res = in;
        try {
            res = UriUtils.encode(in, GeneralConstants.ENCODING_UTF8);
        } catch(UnsupportedEncodingException e){
            log.error("ERROR: unsupported encoding: " + GeneralConstants.ENCODING_UTF8, e);
        }
        return res;
    }
}
Run Code Online (Sandbox Code Playgroud)

这样,您的客户端应用程序将通过重定向接收您的Web应用程序的访问令牌/#/login?access_token=my_access_token&expires_in=seconds_to_expiration,只要您设置相应REDIRECT_PATH_BASESocialAuthenticationSuccessHandler.

我希望它有所帮助.


Mic*_*urt 5

首先,我强烈建议您放弃这种用例的密码授予.
公共客户端(JavaScript,已安装的应用程序)无法保密其客户机密,这就是为什么它们不能被分配一个:任何检查您的JavaScript代码的访问者都可以发现该秘密,从而实现您拥有的相同身份验证页面,将您的用户密码存储在这个过程.

隐性补助已建立正是为了你在做什么.
使用基于重定向的流具有将身份验证机制留给授权服务器的优势,而不是让每个应用程序都有一部分:这主要是单点登录(SSO)的定义.

话虽如此,您的问题与我刚才回答的问题紧密相关:拥有Spring的OAuth2服务器以及第三方OAuth提供商

总结答案:

最后,它是关于授权服务器如何保护AuthorizationEndpoint:/ oauth/authorize.由于您的授权服务器可以工作,因此您已经有一个扩展WebSecurityConfigurerAdapter的配置类,它使用formLogin处理/ oauth/authorize的安全性.这就是你需要整合社交内容的地方.

您根本无法使用密码授予您要实现的目标,您必须将公共客户端重定向到授权服务器.然后,授权服务器将重定向到社交登录,作为/oauth/authorize端点的安全机制.