Отдельный вход в Spring Security Social из REST security - PullRequest
0 голосов
/ 23 апреля 2019

У меня есть диалог входа в систему (вид регистрации) с Facebook и Twitter.С другой стороны, у меня есть API для отдыха, защищенный токеном JWT на основе асимметричной пары ключей.Как я могу защитить свой REST API независимо от своего логина в социальной сети?Не должно быть никакой связи между Social Login и Rest Api.Остальные API должны быть защищены только M2M с Oauth2.

(Весенняя социальная часть вдохновлена ​​https://github.com/callicoder/spring-boot-react-oauth2-social-login-demo)

Это то, что у меня есть сейчас ... и я получаю форму диалога входа в Facebook ....

SecurityConfig.java

@Configuration
@Order(-1) // Ordering is required, otherwise the security configuration is not recognized
@EnableWebSecurity
@EnableGlobalMethodSecurity(
    securedEnabled = true,
    jsr250Enabled = true,
    prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private CustomUserDetailsService customUserDetailsService;

  @Autowired
  private CustomOAuth2UserService customOAuth2UserService;

  @Autowired
  private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;

  @Autowired
  private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;

  @Autowired
  private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;

  @Bean
  public TokenAuthenticationFilter tokenAuthenticationFilter() {
    return new TokenAuthenticationFilter();
  }

  /*
    By default, Spring OAuth2 uses HttpSessionOAuth2AuthorizationRequestRepository to save
    the authorization request. But, since our service is stateless, we can't save it in
    the session. We'll save the request in a Base64 encoded cookie instead.
  */
  @Bean
  public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() {
    return new HttpCookieOAuth2AuthorizationRequestRepository();
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

  @Bean(BeanIds.AUTHENTICATION_MANAGER)
  @Override
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }

  @Override
  public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
    authenticationManagerBuilder
        .userDetailsService(customUserDetailsService)
        .passwordEncoder(passwordEncoder());
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
        .cors()
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .csrf()
        .disable()
        .formLogin()
        .disable()
        .httpBasic()
        .disable()
        .exceptionHandling()
        .authenticationEntryPoint(new RestAuthenticationEntryPoint())
        .and()
        .authorizeRequests()
        .antMatchers("/error",
            "/favicon.ico",
            "/**/*.png",
            "/**/*.gif",
            "/**/*.svg",
            "/**/*.jpg",
            "/**/*.html",
            "/**/*.css",
            "/**/*.js",
            "/actuator/*",
            "/swagger*.yaml")
        .permitAll()
        .antMatchers("/login/**","/auth/**", "/oauth2/**", "/oauth/**")
        .permitAll()
        .anyRequest()
        .authenticated()
        .and()
        .oauth2Login()
        .authorizationEndpoint()
        .baseUri("/oauth2/authorize")
        .authorizationRequestRepository(cookieAuthorizationRequestRepository())
        .and()
        .redirectionEndpoint()
        .baseUri("/oauth2/callback/*")
        .and()
        .userInfoEndpoint()
        .userService(customOAuth2UserService)
        .and()
        .successHandler(oAuth2AuthenticationSuccessHandler)
        .failureHandler(oAuth2AuthenticationFailureHandler);


    // Add our custom Token based authentication filter
    http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
  }

  private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
    enhanceJsonMessageConverter(restTemplate);
    customOAuth2UserService.setRestOperations(restTemplate);
    return customOAuth2UserService;
  }

  private void enhanceJsonMessageConverter(RestTemplate restTemplate) {
    // NOTE:
    // Facebook's UserInfo API -> https://graph.facebook.com/me
    // returns "text/javascript; charset=UTF-8" for the "content-type" response header
    // even though the content is JSON. This is not correct and should be reported to Facebook to fix.
    //
    // This is a temporary workaround that adds "text/javascript; charset=UTF-8"
    // as a supported MediaType in MappingJackson2HttpMessageConverter,
    // which is used to convert the UserInfo response to a Map.

    HttpMessageConverter<?> jsonMessageConverter = restTemplate.getMessageConverters().stream()
        .filter(c -> c instanceof MappingJackson2HttpMessageConverter)
        .findFirst()
        .orElse(null);

    if (jsonMessageConverter == null) {
      return;
    }

    List<MediaType> supportedMediaTypes = new ArrayList<>(jsonMessageConverter.getSupportedMediaTypes());
    supportedMediaTypes.add(MediaType.valueOf("text/javascript;charset=UTF-8"));
    ((AbstractHttpMessageConverter) jsonMessageConverter).setSupportedMediaTypes(supportedMediaTypes);
  }
}

TokenauthenticationFilter.java

public class TokenAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private TokenProvider tokenProvider;

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    private static final Logger logger = LoggerFactory.getLogger(TokenAuthenticationFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            String jwt = getJwtFromRequest(request);
            if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
                Long userId = tokenProvider.getUserIdFromToken(jwt);

                UserDetails userDetails = customUserDetailsService.loadUserById(userId);
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception ex) {
            logger.error("Could not set user authentication in security context", ex);
        }

        filterChain.doFilter(request, response);
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7, bearerToken.length());
        }
        return null;
    }
}

application.yml

spring:
  security:
    oauth2:
      client:
        registration:
          facebook:
            client-id: my-client-id
            client-secret: my-client-secret
            redirectUriTemplate: "{baseUrl}/oauth2/callback/{registrationId}"
            scope:
              - email
              - public_profile
        provider:
          facebook:
            authorization-uri: https://www.facebook.com/v3.0/dialog/oauth
            token-uri: https://graph.facebook.com/v3.0/oauth/access_token
            user-info-uri: https://graph.facebook.com/v3.0/me?fields=id,first_name,middle_name,last_name,name,email,verified,is_verified,picture.width(250).height(250)

security:
  oauth2:
    resource:
      jwt:
        key-value: 
            -----BEGIN PUBLIC KEY-----
            MY-PUBLIC-KEY
            -----END PUBLIC KEY-----
app:
  auth:
    tokenSecret: my-token-secret
    tokenExpirationMsec: 864000000
  oauth2:
    # After successfully authenticating with the OAuth2 Provider,
    # we'll be generating an auth token for the user and sending the token to the
    # redirectUri mentioned by the client in the /oauth2/authorize request.
    # We're not using cookies because they won't work well in mobile clients.
    authorizedRedirectUris:
      - http://localhost:3000/oauth2/redirect
      - myandroidapp://oauth2/redirect
      - myiosapp://oauth2/redirect

Теперь оба токена, один из социального входа и RESTAPI проверяется с помощью TokenauthenticationFilter, и токен из моего REST API, конечно, недействителен, и запрос «error»: «Unauthorized».

Я пытался установить остальные API сопоставления перед .authorizeRequests ()

 .authorizeRequests().antMatchers("/property-finance/**").authenticated()
 .and()
 .authorizeRequests()

Но это приводит к социальному (например, Facebook) диалогу входа.

спасибо за вашу помощь!

...