Intercept request & send to external - spring-boot

I am developing a Spring boot project.
One example of my controller:
#Controller
public class RestController {
#GetMapping(value = "/student/{studentId}")
public #ResponseBody Student getData(#PathVariable Integer studentId) {
Student student = new Student();
student.setName("Peter");
student.setId(studentId);
return student;
}
}
I have other endpoints implemented as well.
I need to intercept every request hits my endpoints & forward the request to another service (microservice), in other words, I need to forward each request to another web app running on the same local machine as current one, based on the response from that service to decide whether proceed forward the request or not.
My rough idea is to use HandlerIntercept , but I am not sure whether I am going to the right direction. Could someone please share some experiences what is the best way to achieve this? It would be nice if you could show some sample code. Thanks in advance.

You can use HandlerInterceptorAdapter.
Define the Interceptor as below.
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object object) throws Exception {
System.out.println("In preHandle we are Intercepting the Request");
System.out.println("____________________________________________");
//Call the Rest API and Validate
if (conditionsNotMet()) {
response.getWriter().write("something");
response.setStatus(someErrorCode);
return false;
}
}
}
Register the HandlerInterceptorAdapter
#Configuration
public class PathMatchingConfigurationAdapter extends WebMvcConfigurerAdapter {
#Autowired
private RequestInterceptor requestInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestInterceptor);
}
}
using WebMvcConfigurer
#Configuration
public class PathMatchingConfigurationAdapter implements WebMvcConfigurer {
#Autowired
private RequestInterceptor requestInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestInterceptor);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) {
}
#Override
public void addCorsMappings(CorsRegistry corsRegistry) {
}
#Override
public void addViewControllers(ViewControllerRegistry viewControllerRegistry) {
}
#Override
public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) {
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {
}
#Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> list) {
}
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> list) {
}
#Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
}
#Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
}
#Override
public Validator getValidator() {
return null;
}
#Override
public MessageCodesResolver getMessageCodesResolver() {
return null;
}
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
#Override
public void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) {
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) {
}
#Override
public void addFormatters(FormatterRegistry formatterRegistry) {
}
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
}

Related

How to troubleshoot and resolve 404 on SockJs's /info path when with cross-origin

I'm having trouble with SockJs and CORS. I use spring. I set up the WebMvcConfigured as follows:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowCredentials(true);
;
}
};
}
and WebSocketConfig as follows:
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/gs-guide-websocket")
.setAllowedOrigins("*")
.withSockJS();
}
However, when my web client tries to GET against /gs-guide-websocket/info , it gets a 404.
SockJs's specification requires a /info path to be present. It seems like even though I did use .withSockJS() on the server side, it did not set up the /gs-guide-websocket/info path.
How should I create this path through spring and/or spring-websocket?
Thank you,
Kevin
I am not using the STOMP protocol, whereas I have configured the websocket with SockJS which is working fine for me.
Here the message payload has sent as response to the frontend.
CustomWebSocketHandler.java
#Component
public class CustomWebSocketHandler extends TextWebSocketHandler {
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("message", "Session started");
jsonObject.addProperty("payload", payload);
session.sendMessage(new TextMessage(new Gson().toJson(jsonObject)));
}
}
WebSocketConfig.java
#EnableWebSocket
#Configuration
public class WebSocketConfig implements WebSocketConfigurer {
#Autowired
private CustomWebSocketHandler customWebSocketHandler;
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry
.addHandler(customWebSocketHandler, "/socket")
.setAllowedOrigins("*")
.withSockJS()
.setHeartbeatTime(20);
}
}

sprin mvc 5 rest + aouth 2 + angular 6 - oauth/token ‘Access-Control-Allow-Origin’ missing

i'm starting an app with spring mvc 5 & angular 6, when i try to get the token by sending a post request with angular it show in my browser console this message:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/gestion/oauth/token. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
this is my spring configuration :
#Configuration
#EnableAuthorizationServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Import(ServerSecurityConfig.class)
public class AuthServerOAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder oauthClientPasswordEncoder;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
public OAuth2AccessDeniedHandler oauthAccessDeniedHandler() {
return new OAuth2AccessDeniedHandler();
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()").passwordEncoder(oauthClientPasswordEncoder);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager).userDetailsService(userDetailsService);
}
}
My Resource Server Configuration class :
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.requestMatchers().antMatchers("/api/**").and().authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/**").access("#oauth2.hasScope('write')")
.anyRequest().access("#oauth2.hasScope('read')");
}
}
This is my Web Security Configurer class:
#Configuration
#EnableWebSecurity
#Import(Encoders.class)
public class ServerSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder userPasswordEncoder;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(userPasswordEncoder);
}
}
This my APP Initializer:
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {
AppConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected Filter[] getServletFilters() {
Filter [] filters = {new CorsFilter()};
return filters;
}
}
My Custom Filter:
public class CorsFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
chain.doFilter(request, response);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
My angular service:
public login(credentials: any): Observable<any> {
const body = `username=${credentials.username}&password=${credentials.password}&grant_type=password&client_id=admin`;
return this.httpClient.post<any>(this.URL, body, { headers: new HttpHeaders({
'Authorization': `Basic ${btoa('admin:admin')}`,
'Content-type': 'application/x-www-form-urlencoded'
})});
}
my question is how to make Angular HttpClient send this post request to generate token
You need to define proxy.conf.json
we can do
{
"/serverapplicationName/*": {
"target": "http://localhost:8080",
"secure": false,
"logLevel":"debug",
"changeOrigin": true
}
}
and please run by npm "npm start"
Try to make highest priority to your CorsFilter implementation.
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsFilter implements Filter {
...
}

What is the ServletFilter equalent of a RabbitMq Listener?

I have a spring-boot web application for which I implemented an MDCFilter that adds a UUID to MDC logging context that i can find in the log file.
The Filter class looks like this.
public class MDCFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
String requestId = UUID.randomUUID().toString();
MDC.put(REQUEST_ID_KEY, requestId);
response.addHeader("trace", requestId);
try {
chain.doFilter(req, resp);
} finally {
MDC.remove("trace");
}
}
#Override
public void destroy() {
}
}
But recently we moved towards processing traffic via Queues and I have no clue from the documents to replicate this filter behaviour for the message listeners.
My listener would look something like this.
#RabbitListener(queues = "${queue1}")
public void receiveMessages(Message message) {
doTheBusinessLogic(message)
}
Can anyone point me to the right direction ?
Use the container's adviceChain. Assuming you are using Boot 2.0 and the simple container factory, override boot's factory to add the advice...
#SpringBootApplication
public class So49770881Application {
public static void main(String[] args) {
SpringApplication.run(So49770881Application.class, args);
}
#Bean(name = "rabbitListenerContainerFactory")
public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(
SimpleRabbitListenerContainerFactoryConfigurer configurer,
ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setAdviceChain(new MDCAdvice());
return factory;
}
public static class MDCAdvice implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// pre process
try {
return invocation.proceed();
}
finally {
// post process
}
}
}
}

Spring Websocket Configuration: using WebSocketMessageBrokerConfigurationSupport and WebSocketConfigurer together - how?

I am configuring currently my Spring Websocket using the class
public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport
now I came across the advice Spring STOMP Websockets: any way to enable permessage-deflate on server side?
that makes use of
public class SampleJettyWebSocketsApplication implements WebSocketConfigurer
and overrides
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry)
and offers
#Bean
public DefaultHandshakeHandler handshakeHandler()
Question, what is the relation between WebSocketConfigurer and WebSocketMessageBrokerConfigurationSupport? In other words, can I possibly somehow add configuration from WebSocketConfigurer implementation via API of the first class, WebSocketMessageBrokerConfigurationSupport, so all configuration remains in one single file?
The WebSocketMessageBrokerConfigurationSupport implementation is DelegatingWebSocketMessageBrokerConfiguration which is configured via #EnableWebSocketMessageBroker. All you need in your custom code is WebSocketMessageBrokerConfigurer implementation. And that one is injected into DelegatingWebSocketMessageBrokerConfiguration:
#Autowired(required = false)
public void setConfigurers(List<WebSocketMessageBrokerConfigurer> configurers) {
This is a sample config from my test-cases:
#Configuration
#EnableWebSocketMessageBroker
static class ServerConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Bean
public DefaultHandshakeHandler handshakeHandler() {
return new DefaultHandshakeHandler(new TomcatRequestUpgradeStrategy());
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setHandshakeHandler(handshakeHandler())
.setAllowedOrigins("http://foo.com")
.addInterceptors(new HandshakeInterceptor() {
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
return request.getHeaders().getOrigin() != null;
}
#Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception) {
}
})
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry configurer) {
configurer.setApplicationDestinationPrefixes("/app")
.enableSimpleBroker("/topic", "/queue");
}
}

spring custom mongo TokenStore not refresh new access_token automatically when old expired?

I use mongo custom tokenStore and codeService:
this is my custom mongoTokenStore:
public class MongoTokenStore implements TokenStore {
private final MongoAccessTokenRepository mongoAccessTokenRepository;
private final MongoRefreshTokenRepository mongoRefreshTokenRepository;
private AuthenticationKeyGenerator authenticationKeyGenerator =
new DefaultAuthenticationKeyGenerator();
public MongoTokenStore(MongoAccessTokenRepository mongoAccessTokenRepository, MongoRefreshTokenRepository mongoRefreshTokenRepository) {
this.mongoAccessTokenRepository = mongoAccessTokenRepository;
this.mongoRefreshTokenRepository = mongoRefreshTokenRepository;
}
#Override
public OAuth2Authentication readAuthentication(OAuth2AccessToken oAuth2AccessToken) {
return readAuthentication(oAuth2AccessToken.getValue());
}
#Override
public OAuth2Authentication readAuthentication(String tokenId) {
return mongoAccessTokenRepository.findByTokenId(tokenId)
.map(MongoAccessToken::getAuthentication)
.orElse(null);
}
#Override
public void storeAccessToken(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
MongoAccessToken mongoAccessToken = new MongoAccessToken(oAuth2AccessToken, oAuth2Authentication,
authenticationKeyGenerator.extractKey(oAuth2Authentication));
mongoAccessTokenRepository.save(mongoAccessToken);
}
#Override
public OAuth2AccessToken readAccessToken(String tokenValue) {
return mongoAccessTokenRepository.findByTokenId(tokenValue)
.map(MongoAccessToken::getOAuth2AccessToken)
.orElse(null);
}
#Override
public void removeAccessToken(OAuth2AccessToken oAuth2AccessToken) {
mongoAccessTokenRepository.findByTokenId(oAuth2AccessToken.getValue())
.ifPresent(mongoAccessTokenRepository::delete);
}
#Override
public void storeRefreshToken(OAuth2RefreshToken oAuth2RefreshToken, OAuth2Authentication oAuth2Authentication) {
MongoRefreshToken token=new MongoRefreshToken(oAuth2RefreshToken,oAuth2Authentication);
mongoRefreshTokenRepository.save(token);
}
#Override
public OAuth2RefreshToken readRefreshToken(String tokenValue) {
return mongoRefreshTokenRepository.findByTokenId(tokenValue)
.map(MongoRefreshToken::getOAuth2RefreshToken)
.orElse(null);
}
#Override
public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken oAuth2RefreshToken) {
return mongoRefreshTokenRepository.findByTokenId(oAuth2RefreshToken.getValue())
.map(MongoRefreshToken::getAuthentication)
.orElse(null);
}
#Override
public void removeRefreshToken(OAuth2RefreshToken oAuth2RefreshToken) {
mongoRefreshTokenRepository.findByTokenId(oAuth2RefreshToken.getValue())
.ifPresent(mongoRefreshTokenRepository::delete);
}
#Override
public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken oAuth2RefreshToken) {
mongoAccessTokenRepository.findByRefreshToken(oAuth2RefreshToken.getValue())
.ifPresent(mongoAccessTokenRepository::delete);
}
#Override
public OAuth2AccessToken getAccessToken(OAuth2Authentication oAuth2Authentication) {
return mongoAccessTokenRepository.findByAuthenticationId(authenticationKeyGenerator
.extractKey(oAuth2Authentication))
.map(MongoAccessToken::getOAuth2AccessToken)
.orElse(null);
}
#Override
public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String s, String s1) {
return mongoAccessTokenRepository.findByClientIdAndUserName(s,s1)
.stream()
.map(MongoAccessToken::getOAuth2AccessToken)
.collect(Collectors.toList());
}
#Override
public Collection<OAuth2AccessToken> findTokensByClientId(String s) {
return mongoAccessTokenRepository.findByClientId(s)
.stream()
.map(MongoAccessToken::getOAuth2AccessToken)
.collect(Collectors.toList());
}
}
and this is my custom mongoCodeService:
#Component
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class MongoAuthenticationCodeService extends RandomValueAuthorizationCodeServices{
private final MongoAuthenticationCodeRepository repository;
#Override
protected void store(String code, OAuth2Authentication oAuth2Authentication) {
repository.save(new MongoAuthenticationCode(code,oAuth2Authentication));
}
#Override
protected OAuth2Authentication remove(String code) {
return repository.findOneByCode(code)
.map(MongoAuthenticationCode::getAuthentication)
.orElse(null);
}
}
and my OAuth2Config:
#Configuration
public class OAuth2ServerConfiguration {
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends
ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(READ_AND_WRITE_RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/webjars/**", "/oauth/authorize/**", "/", "/customLogout",
"/oauth/check_token/**", "/login").permitAll()
.mvcMatchers(HttpMethod.GET, "/loginAttemptUsers").hasRole("ADMIN")
.anyRequest().authenticated();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
private CustomUserDetailService customUserDetailService;
#Autowired
private MongoAuthenticationCodeService mongoAuthenticationCodeService;
#Autowired
private MongoTokenStore mongoTokenStore;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.authorizationCodeServices(mongoAuthenticationCodeService)
.tokenStore(mongoTokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(customUserDetailService)
.approvalStoreDisabled();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("kksdi2388wmkwe")
.authorizedGrantTypes("authorization_code", "password", "refresh_token")
.scopes("read", "write")
.resourceIds("ReadAndWriteResource")
.secret("kksd23isdmsisdi2")
.autoApprove(true)
.accessTokenValiditySeconds(120)
.refreshTokenValiditySeconds(1200);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore);
return tokenServices;
}
}
}
the problem is:
I can login from spring zuul ui-server by "authorization_code",and can
access ResourceServer data .
But when the access_token expired(120s), while re-getting ResourceServer data, I see mongoTokenStore removed the existed access_token , but why not refresh new access_token automatically?
How to resolve it?
The whole point of refresh token is to add one more security dimension to OAuth2.
When the end user has acquired access token, he now can access any resource within the authorization rules that the token awards him.
If his access token is stolen somehow, the attacker can now access to any resource that the access token authorizes him, but if we set an expiry time for the access token the attacker will have limited access time.
With that being said, if spring oauth2 would have refresh that token automatically that one more security aspect would not be applied, and the whole idea of refreshing the token would be wasted.
So, to conclude it's your responsibility to make sure that your end user is re-authorize via OAuth2 again.
You can also read this:
Why Does OAuth v2 Have Both Access and Refresh Tokens?

Resources