Spring boot: How to add interceptors to static resources? - java

I have several folders in /static/img/** and I need to add interceptors to some of them to check user permissions. I've used interceptors earlier and added them this way:
#SpringBootApplication
#EnableTransactionManagement
public class Application extends WebMvcConfigurerAdapter {
...
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
#Bean
public AuthHeaderInterceptor authHeaderInterceptor() {
return new AuthHeaderInterceptor();
}
#Bean
public AuthCookieInterceptor authCookieInterceptor() {
return new AuthCookieInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry
.addInterceptor(authHeaderInterceptor())
.addPathPatterns(REST_URL)
.excludePathPatterns(
new String[] {
REST_SECURITY_URL,
REST_SETTINGS_URL,
REST_REPORTS_URL
}
);
registry
.addInterceptor(authCookieInterceptor())
.addPathPatterns(REST_REPORTS_URL);
}
}
All works fine for rest controllers and their URLs, but now I need to secure some static resources and I added this:
#SpringBootApplication
#EnableTransactionManagement
public class Application extends WebMvcConfigurerAdapter {
...
#Bean
public RoleAdminInterceptor roleAdminInterceptor() {
return new RoleAdminInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry
.addInterceptor(authHeaderInterceptor())
.addPathPatterns(REST_URL)
.excludePathPatterns(
new String[] {
REST_SECURITY_URL,
REST_SETTINGS_URL,
REST_REPORTS_URL
}
);
//THIS NOT WORK
registry
.addInterceptor(roleAdminInterceptor())
.addPathPatterns("/static/img/admin/**");
registry
.addInterceptor(authCookieInterceptor())
.addPathPatterns(REST_REPORTS_URL);
}
}
Commented line doesn't work. When I send request to /static/img/admin/test.png RoleAdminInterceptor is never called.
What I'm doing wrong?

I know this is an old question, but since it's unanswered it might help others searching for it.
This is what worked for me:
1- Declare an interceptor class:
class RoleBasedAccessInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
AntPathMatcher matcher = new AntPathMatcher();
String pattern = "/static/img/admin/**";
String requestURI = request.getRequestURI();
if (matcher.match(pattern, requestURI)) {
//Do whatever you need
return validateYourLogic();
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
2- Configure WebMvcConfigurer
public class WebMvcConfiguration implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RoleBasedAccessInterceptor());
}
}

I think in this case you could use Filters with Spring Security instead of Interceptors as you could Validate the access earlier on the process even before hitting the Interceptor, unless there is a specific use case that you need to use the interceptor here.
Some topic about the difference between these two:
filters-vs-interceptor

Related

Injecting Interceptor jar file to my spring boot projects

I am trying to create a jar file to inject into any of my spring boot project for logging the request details.
I am able to do this in one of my project. You can see the code below.
How to create the jar out of it and how to inject into other projects?
#Component
public class Interceptor extends HandlerInterceptorAdapter {
private static Logger log = LoggerFactory.getLogger(Interceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// log.info("Inside prehandle");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// log.info("Inside postHandle");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("Inside afterCompletion");
sendToLoggerApi(request, response);
}
}
#Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
#Autowired
Interceptor interceptor;
#Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(interceptor);
}
}

How to identify the owner of Session - Spring-Boot Websockets

I am using websockets without Stomp. What is the correct way to decide to whom USer WebSocketSession belongs to?
In my WsConfig i use:
#Configuration
#EnableWebSocket
public class WebSocketServerConfiguration implements WebSocketConfigurer {
protected final CustomWebSocketHandler webSocketHandler;
#Autowired
public WebSocketServerConfiguration(CustomWebSocketHandler webSocketHandler) {
this.webSocketHandler = webSocketHandler;
}
#SuppressWarnings("NullableProblems")
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler, "/ws")
.addInterceptors();
}
}
my WsHandler is currently empty:
#Service
public class SplitBillWebSocketHandler extends TextWebSocketHandler {
#Override
public void handleTransportError(WebSocketSession session, Throwable throwable) throws Exception {
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//determine User for session
User user = determineUser(session);
sessionStorage.add(user.getName(),session);
}
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage jsonTextMessage) throws Exception {
}
}
What is the way to determine the user? Or what is the best practice to do this?
Do I need to pass some parameter to websocket URL from client ( which isn't standard as far as I am aware ), or how to identify the session?
Thanks for help!

Swagger - Custom authentication for /api-docs

We have have a Spring 5, non-Spring Boot application, using Springfox 2.9.2 + Swagger UI.
I don't know how to secure /api-docs endpoint: I'd like it to call my authentication function each time it's accessed. I made it work for swagger-ui.html, but without success for /api-docs. Here's what I got.
#Configuration
#EnableSwagger2
#EnableWebMvc
public class SwaggerConfig implements WebMvcConfigurer {
#Autowired
protected AuthService authService;
#Override
public void addViewControllers(ViewControllerRegistry registry) {
// registry.addViewController("/docs/swagger/api-docs"); doesnt work
registry.addRedirectViewController("/docs/swagger/swagger-resources/configuration/ui", "/swagger-resources/configuration/ui");
registry.addRedirectViewController("/docs/swagger/swagger-resources/configuration/security", "/swagger-resources/configuration/security");
registry.addRedirectViewController("/docs/swagger/swagger-resources", "/swagger-resources");
}
class Interceptor implements HandlerInterceptor{
#Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler ) {
try{
authService.assertAdmin(); // I need to call this
}catch (Exception e){
return false;
}
return true;
}
}
#Override
public void addInterceptors( final InterceptorRegistry registry) {
registry.addInterceptor(new Interceptor());
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// docs/swagger/index.html
registry.addResourceHandler("/docs/swagger/swagger-ui.html**")
.addResourceLocations("classpath:/META-INF/resources/swagger-ui.html");
// docs/swagger/webjars
registry.addResourceHandler("/docs/swagger/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
Another option would be to close access to /api-docs permanently and just directly call the method that generates JSON from some new endpoint. Would that be possible?
Eventually I solved this by spring security, as #UsamaAmjad proposed.
open class SecurityInitializer : AbstractSecurityWebApplicationInitializer()
#Configuration
#EnableWebSecurity
open class SecurityConfig : WebSecurityConfigurerAdapter() {
#Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http.antMatcher("/docs/swagger/v2/api-docs").addFilter(myFilter())
}
open fun myFilter() = object : FilterSecurityInterceptor() {
override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain?) {
if (do your stuff here) {
chain!!.doFilter(request,response) // continue with other filters
} else {
super.doFilter(request, response, chain) // filter this request
}
}
}
}

Use #PathParam(javax.websocket.server.PathParam) in WebSocketConfigurer for Spring Boot application

I have created a spring boot application in which I want to use Web Sockets. When I am using it without parameters its working fine. Below is the code without the parameters
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ABC(), "/getABC").setAllowedOrigins("*");
registry.addHandler(new XYZ(), "/getXYZ").setAllowedOrigins("*");
}
}
But now I need to pass a parameter to it using #PathParam. I am not able to use it in this configuration like
registry.addHandler(new XYZ(), "/getXYZ{someId}").setAllowedOrigins("*");
My Handler code:
public class XYZ extends TextWebSocketHandler {
static List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
String someId;
public XYZ() {
}
public XYZ(#PathParam(value = "someId") String someId) {
this.someId= someId;
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// the messages will be broadcasted to all users.
sessions.add(session);
}
}
I think there is some problem with the syntax, try using
public XYZ(#PathParam("someId") String someId)

How to add filter after the HTTP BasicAuthenticationFilter when using #EnableAuthorizationServer

I am trying to go over the following documentation: https://github.com/spring-projects/spring-security-oauth/blob/f25592e682303b0cf89e1d7555174bac18e174df/docs/oauth2.md#mapping-user-roles-to-scopes
In the documentation, it says in order to map user roles to scopes, along with setting the checkUserScopes=true in the DefaultOAuth2RequestFactory, we need to add the TokenEndpointAuthenticationFilter filter after the HTTP BasicAuthenticationFilter. I was wondering how that could be done.
Here is what my AuthorizationServer looks like:
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends
AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private OAuth2RequestFactory requestFactory;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager);
endpoints.requestFactory(requestFactory);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.withClientDetails(clientDetailsService());
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.checkTokenAccess("isAuthenticated()");
}
#Bean
public ClientDetailsService clientDetailsService() {
Map<String, ClientDetails> clientDetailsStore = new HashMap<String, ClientDetails>();
Collection<String> scope = new HashSet<String>();
scope.add("user");
scope.add("admin");
Collection<String> authorizedGrantTypes = new HashSet<String>();
authorizedGrantTypes.add("password");
authorizedGrantTypes.add("refresh_token");
BaseClientDetails clientDetails = new BaseClientDetails();
clientDetails.setClientId("client");
clientDetails.setClientSecret("secret");
clientDetails.setScope(scope);
clientDetails.setAuthorizedGrantTypes(authorizedGrantTypes);
clientDetailsStore.put("client", clientDetails);
InMemoryClientDetailsService clientDetailsService = new InMemoryClientDetailsService();
clientDetailsService.setClientDetailsStore(clientDetailsStore);
return clientDetailsService;
}
#Bean
public OAuth2RequestFactory requestFactory() {
DefaultOAuth2RequestFactory requestFactory =
new DefaultOAuth2RequestFactory(clientDetailsService());
requestFactory.setCheckUserScopes(true);
return requestFactory;
}
}
Also, it would be fantastic to provide a sample CURL on how we can test the grant-type password.
Appreciate any help!
Instead of using #EnableAuthorizationServer you should be able to extend AuthorizationServerSecurityConfiguration and include that in your Spring configuration. E.g.
#Configuration
public class OAuth2Config extends AuthorizationServerSecurityConfiguration {
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.addFilterAfter(myFilter(), BasicAuthenticationFilter.class);
}
}
You can add also add additional filters via the AuthorizationServerSecurityConfigurer, though they come before Basic auth, not after.
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.addTokenEndpointAuthenticationFilter(myFilter());
security.checkTokenAccess("isAuthenticated()");
}
Adds a new custom authentication filter for the TokenEndpoint. Filters will be set upstream of the default BasicAuthenticationFilter.

Categories