У меня есть диалог входа в систему (вид регистрации) с 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) диалогу входа.
спасибо за вашу помощь!