Integrate Spring Security OAuth2 and Spring Social

前端 未结 4 1203
终归单人心
终归单人心 2020-12-07 21:07

I\'m working with a Spring Boot + Spring Security OAuth2 application that I believe was inspired by examples from Dave Syer. The application is configured to be an OAuth2 au

4条回答
  •  -上瘾入骨i
    2020-12-07 21:13

    I was starting with the good answer of above (https://stackoverflow.com/a/33963286/3351474) however with my version of Spring Security (4.2.8.RELEASE) this fails. The reason is that in org.springframework.security.access.intercept.AbstractSecurityInterceptor#authenticateIfRequired the PreAuthenticatedAuthenticationToken of the answer is not authenticated. Some GrantedAuthorities have to be passed. In addition sharing the token in an URL parameter is not good, it should always be hidden in an HTTPs payload or header. Instead a HTML template is loaded and the token value is inserted into a ${token} placeholder field.

    Here the revised version:

    NOTE: The used UserDetails here is implementing org.springframework.security.core.userdetails.UserDetails

    @Component
    public class SocialAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    
        @Autowired
        private OAuth2TokenStore tokenStore;
    
        @Qualifier("tokenServices")
        @Autowired
        private AuthorizationServerTokenServices authTokenServices;
    
        public void onAuthenticationSuccess(HttpServletRequest request,
                                            HttpServletResponse response, Authentication authentication)
                throws IOException, ServletException {
            IClient user = ((SocialUserDetails) authentication.getPrincipal()).getUser();
            // registration is not finished, forward the user, a marker interface 
            // IRegistration is used here, remove this if there no two step approach to 
            // create a user from a social network
            if (user instanceof IRegistration) {
                response.sendRedirect(subscriberRegistrationUrl + "/" + user.getId());
            }
            OAuth2AccessToken token = loginUser(user);
            // load a HTML template from the class path and replace the token placeholder within, the HTML should contain a redirect to the actual page, but must store the token in a safe place, e.g. for preventing CSRF in the `sessionStorage` JavaScript storage.
            String html = IOUtils.toString(getClass().getResourceAsStream("/html/socialLoginRedirect.html"));
            html = html.replace("${token}", token.getValue());
            response.getOutputStream().write(html.getBytes(StandardCharsets.UTF_8));
        }
    
        private OAuth2Authentication convertAuthentication(Authentication authentication) {
            OAuth2Request request = new OAuth2Request(null, authentication.getName(),
                    authentication.getAuthorities(), true, null,
                    null, null, null, null);
            // note here the passing of the authentication.getAuthorities()
            return new OAuth2Authentication(request,
                    new PreAuthenticatedAuthenticationToken(authentication.getPrincipal(), "N/A",  authentication.getAuthorities())
            );
        }
    
        /**
         * Logs in a user.
         */
        public OAuth2AccessToken loginUser(IClient user) {
            SecurityContext securityContext = SecurityContextHolder.getContext();
            UserDetails userDetails = new UserDetails(user);
            Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, "N/A", userDetails.getAuthorities());
            securityContext.setAuthentication(authentication);
            OAuth2Authentication oAuth2Authentication = convertAuthentication(authentication);
            // delete the token because the client id in the DB is calculated as hash of the username and client id (here also also identical to username), this would be identical to the
            // to an existing user. This existing one can come from a user registration or a previous user with the same name.
            // If a new entity with a different ID is used the stored token hash would differ and the the wrong token would be retrieved 
            tokenStore.deleteTokensForUserId(user.getUsername());
            OAuth2AccessToken oAuth2AccessToken = authTokenServices.createAccessToken(oAuth2Authentication);
            // the DB id of the created user is returned as additional data, can be 
            // removed if not needed
            ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(new HashMap<>());
            oAuth2AccessToken.getAdditionalInformation().put("userId", user.getId());
            return oAuth2AccessToken;
        }
    
    }
    

    Example socialLoginRedirect.html:

    
    
    
        
        Example App
        
    
    
    
    

    Please follow this link.

    The configuration wiring in a WebSecurityConfigurerAdapter:

    @Configuration
    @EnableWebSecurity
    @EnableWebMvc
    @Import(WebServiceConfig.class)
    public class AuthenticationConfig extends WebSecurityConfigurerAdapter {
    
        @Value("${registrationUrl}")
        private String registrationUrl;
    
        @Autowired
        private SocialAuthenticationSuccessHandler socialAuthenticationSuccessHandler;
    
        @Value("${loginUrl}")
        private String loginUrl;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            List permitAllUrls = new ArrayList<>();
            // permit social log in
            permitAllUrls.add("/auth/**");
            http.authorizeRequests().antMatchers(permitAllUrls.toArray(new String[0])).permitAll();
    
            SpringSocialConfigurer springSocialConfigurer = new SpringSocialConfigurer();
            springSocialConfigurer.signupUrl(registrationUrl);
            springSocialConfigurer.postFailureUrl(loginUrl);
            springSocialConfigurer
                    .addObjectPostProcessor(new ObjectPostProcessor() {
                        @SuppressWarnings("unchecked")
                        public SocialAuthenticationFilter postProcess(SocialAuthenticationFilter filter){
                            filter.setAuthenticationSuccessHandler(socialAuthenticationSuccessHandler);
                            return filter;
                        }
                    });
            http.apply(springSocialConfigurer);
    
            http.logout().disable().csrf().disable();
    
            http.requiresChannel().anyRequest().requiresSecure();
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }
    

提交回复
热议问题