I have implemented custom Logout handler in grails. For some reasons, it is not working. I have checked a similar question on stackoverflow
spring security logout handler not working ,but it did not work for me. The problem is my custom method is not being called.
My code is as follows:
Config.groovy
grails.plugin.springsecurity.filterChain.chainMap = [
'/api/v1/login/**': 'authenticationProcessingFilter,restAuthenticationFilter,restTokenValidationFilter,filterInvocationInterceptor,restExceptionTranslationFilter',
'/api/v1/logout/**': 'authenticationProcessingFilter,restAuthenticationFilter,restExceptionTranslationFilter,restLogoutFilter',
'/**': 'securityContextPersistenceFilter,anonymousAuthenticationFilter,restAuthenticationFilter,restTokenValidationFilter,restExceptionTranslationFilter,filterInvocationInterceptor']
grails.plugin.springsecurity.useSecurityEventListener = true
grails.plugin.springsecurity.logout.handlerNames =
['rememberMeServices', 'myHandler' ,
'securityEventListener',
'securityContextLogoutHandler']
resources.groovy
beans = {
myHandler(com.as.rest.handlers.CustomSecurityContextLogoutHandler) }
CustomSecurityContextLogoutHandler.groovy
package com.as.rest.handlers
import org.springframework.security.core.Authentication
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
class CustomSecurityContextLogoutHandler extends SecurityContextLogoutHandler {
#Override
void logout(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) {
println("#############################################")
println("#############################################")
println("#############################################")
println("logout")
}
}
Related
I need to add a RequestInterceptor to a specific feign client. The interceptor will add auth information that I do not want to leak to a third party, hence I do not want it to trigger for ALL Feign clients. I have this working, but this seems a tad messy, and am hoping there is a cleaner (less code) option.
I am hoping someone can point to where I can simplify things. Particularly around the encoder/decoder stuff. I really dislike them cluttering up my services constructor like that and find it odd that they even need to be specified in the first place.
I have
// build.gradle
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
I have a RequestInterceptor as
import feign.RequestInterceptor;
import feign.RequestTemplate;
public class BearerAuthRequestInterceptor implements RequestInterceptor {
#Override
public void apply(RequestTemplate requestTemplate) {
// ... SNIP ... (just adds an Authorization header)
}
}
I have a FeignClient as
#FeignClient(name = "myfeignclient")
public interface MyFeignClient {
#GetMapping(value = "/doTheThing")
String doTheThing();
}
I use my FeignClient from a service like so:
#Service
#Import(FeignClientsConfiguration.class)
public class MyService {
private final MyFeignClient myFeignClient;
#Autowired
public MyService(Decoder decoder, Encoder encoder, Contract contract) {
this.myFeignClient = Feign.builder()
.contract(contract)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new BearerAuthRequestInterceptor())
.target(MyFeignClient.class, "https://whatever.com");
}
public void callTheFeignClient() {
myFeignClient.doTheThing();
}
}
Thanks to this comment, I managed to tidy up my implementation a little bit. So no more need for specifying encode/decoder nonsense, or having to manually build my Feign client.
The docs here provide some info, but as is typical they are a bit thin on concrete examples, so perhaps the below will help someone else. Note: I'm using spring boot, and including feign like so in build.gradle implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
First, create the RequestInterceptor like so:
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
import feign.RequestInterceptor;
import lombok.extern.slf4j.Slf4j;
/**
* A Feign configuration to add the incoming Bearer token to an outgoing feign client request.
* Only annotate this class with "#Configuration" if you want this interceptor to apply globally to all your Feign clients.
* Otherwise you risk exposing the auth token to a third party, or adding it unnecessarily to requests that don't need it.
*/
#Slf4j
public class BearerAuthFeignConfig {
#Bean
public RequestInterceptor bearerAuthRequestInterceptor() {
return requestTemplate -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() instanceof Jwt) {
Jwt jwt = (Jwt) authentication.getPrincipal();
requestTemplate.header("Authorization", "Bearer " + jwt.getTokenValue());
} else {
log.error("Unable to add Authoriation header to Feign requestTemplate");
}
};
}
}
Then when declaring your feign client, pass the configuration
#FeignClient(
name = "my-client-that-needs-the-auth",
configuration = BearerAuthFeignConfig.class,
url = "http://whatever.com"
)
public interface PlayerManagementClient {
...
You'll also need the #EnableFeignClients annotation on your #SpringBootApplication class
I was trying to simplify the code of security checks in my grails app and I found that there is a way to drive the security on a service class.
Some of the references I found related to that:
https://www.mscharhag.com/grails/spring-security-call-bean-method-in-spel-expression
Grails custom security evaluator
and some others...
So I tried wiring everything in and seems pretty straightforward, but when I am configuring my custom beans into resources.groovy I am getting this error.
A component required a bean named 'parameterNameDiscoverer' that could not be found.
My resources.groovy looks like this:
import com.auth0.client.auth.AuthAPI
import grails.plugin.springsecurity.rest.RestAuthenticationProvider
import priz.auth0.Auth0APIService
import priz.auth0.Auth0TokenStorageService
import priz.auth0.Auth0TokenVerificationService
import priz.auth0.Auth0UserResolverService
import priz.security.GrailsBeanResolver
import priz.security.GrailsExpressionHandler
import priz.security.UserPasswordEncoderListener
// Place your Spring DSL code here
beans = {
expressionHandler(GrailsExpressionHandler) {
beanResolver = ref('beanResolver')
parameterNameDiscoverer = ref('parameterNameDiscoverer')
permissionEvaluator = ref('permissionEvaluator')
roleHierarchy = ref('roleHierarchy')
trustResolver = ref('authenticationTrustResolver')
}
beanResolver(GrailsBeanResolver) {
grailsApplication = ref('grailsApplication')
}
userPasswordEncoderListener(UserPasswordEncoderListener)
authApi(AuthAPI) { beanDefinition ->
beanDefinition.constructorArgs = [
'${priz.auth0.api.domain}',
'${priz.auth0.api.clientId}',
'${priz.auth0.api.clientSecret}'
]
}
auth0APIService(Auth0APIService) {
authAPI = ref('authApi')
}
auth0TokenVerificationService(Auth0TokenVerificationService)
auth0UserResolverService(Auth0UserResolverService)
tokenStorageService(Auth0TokenStorageService) {
jwtService = ref('jwtService')
userDetailsService = ref('userDetailsService')
auth0TokenVerificationService = ref('auth0TokenVerificationService')
auth0APIService = ref('auth0APIService')
auth0UserResolverService = ref('auth0UserResolverService')
}
/* restAuthenticationProvider */
restAuthenticationProvider(RestAuthenticationProvider) {
tokenStorageService = ref('tokenStorageService')
useJwt = false
jwtService = ref('jwtService')
}
}
Of course, I don't have parameterNameDiscoverer specifically defined in the resources, but I expected that since I didn't customize any of these dependencies are already provided by the Spring Security plugins. But it seems like they cannot be found.
What am I missing? Do I need to define the entire dependency tree in resources?
This should be as simple as creating the bean instance of the implementation you want by adding something like the following to your resources.groovy
parameterNameDiscoverer(DefaultSecurityParameterNameDiscoverer)
Assuming you want the default implementation that is provided with Spring Security (v3.2+). It's not clear what implementation you want, so you may browse the java docs.
Following some other posts, I tried to override the authentication success method of the spring-security handler, but it's never being called. My code looks like:
src/groovy/mypackage/MyAuthenticationSuccessHandler.groovy:
package mypackage
import org.springframework.security.core.Authentication
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler
import javax.servlet.ServletException
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
public MyAuthenticationSuccessHandler() {
println("constructed!")
}
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
println("override called")
super.onAuthenticationSuccess(request, response, authentication);
}
}
resources.groovy:
authenticationSuccessHandler(MyAuthenticationSuccessHandler) {
def conf = SpringSecurityUtils.securityConfig
requestCache = ref('requestCache')
defaultTargetUrl = conf.successHandler.defaultTargetUrl
alwaysUseDefaultTargetUrl = conf.successHandler.alwaysUseDefault
targetUrlParameter = conf.successHandler.targetUrlParameter
useReferer = conf.successHandler.useReferer
redirectStrategy = ref('redirectStrategy')
}
There are no errors, the constructor is definitely called and MyAuthenticationSuccessHandler is injected into a test controller, but onAuthenticationSuccess is never called. I dropped a breakpoint into the superclass version and that worked. I also tried rewriting my custom class in java but that didn't work.
What am I doing wrong?
Turns out another login filter was already active and it was preventing the normal method from working. The filter in question is org.mitre.openid.connect.client.OIDCAuthenticationFilter and the workaround is to inject your success handler through that one e.g.:
authenticationSuccessHandler(apipulse.MyAuthenticationSuccessHandler) {
clientRegistrationTemplate = ref(clientRegistrationTemplate)
}
...
openIdConnectAuthenticationFilter(OIDCAuthenticationFilter) {
...
authenticationSuccessHandler = ref('authenticationSuccessHandler')
}
Just wasted a day looking at this - thanks a bunch, spring.
I'm new to Spring and I try to create a secured rest application using Spring Boot and Spring Security. I'm searching for weeks for a solution now...
I'm using Spring Boots embedded web container (Tomcat) and the spring-boot-starter-parent 1.2.6.RELEASE in my pom.
My endpoints:
/login (to authenticate)
/application/{id} (some service which I want to secure)
I configured my servlet path in my application.properties like this:
server.servletPath: /embedded
so I expect my services e.g. on //localhost/embedded/login
Ok so now the problem: If I run the application without security everything is fine, I can call http//localhost/embedded/application and get an answer.
If I now add my security configuration like this:
import javax.servlet.ServletContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#Configuration
#EnableWebMvcSecurity
#EnableScheduling
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private TokenAuthenticationService tokenAuthenticationService;
#Value("${server.servletPath}")
private String servletPath;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/hello/**", "/login").permitAll()
.antMatchers("/application/**").authenticated().and()
.addFilterBefore(new TokenAuthenticationFilter(tokenAuthenticationService), UsernamePasswordAuthenticationFilter.class);
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.httpBasic().disable();
}
}
when running the application //localhost/application/{id} is secured instead of
//localhost/embedded/application/{id} as I would have expected.
For some reason the servlet path is ignored there. I tought "ok so I just add the servlet path manually" and make it look like this:
...antMatchers(servletPath+"/application/**").authenticated()...
This works in my application. However I also use MockMvc to test my services and for some reason there the servlet path is correctly added to the matchers. So if I start the tests the security filters are mapped to //localhost/embedded/embedded/application/{id} while the controllers themselves still are mapped to //localhost/embedded/application/{id} which is very annoying...
I took a look at here http://spring.io/blog/2013/07/03/spring-security-java-config-preview-web-security/ and thought I could fix the issue by using AbstractSecurityWebApplicationInitializer instead of SpringBootServletInitializer but it changed nothing.
This is my application class by the way:
com.sebn.gsd.springservertemplate.service.security.WebSecurityConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
System.out.println("Run from main");
SpringApplication.run(applicationClass, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass, WebSecurityConfig.class);
}
private static Class<Application> applicationClass = Application.class;
}
The application.properties doesn't contain any more interesting information I think. To be complete this is my MockMvc testing class:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sebn.gsd.springservertemplate.service.api.LoginData;
import com.sebn.gsd.springservertemplate.service.security.Session_model;
import com.sebn.gsd.springservertemplate.service.security.WebSecurityConfig;
import java.util.Arrays;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.Matchers.notNullValue;
import org.junit.Assert;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.web.servlet.ResultActions;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
import org.springframework.web.context.WebApplicationContext;
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {Application.class, WebSecurityConfig.class })
#WebAppConfiguration
#ActiveProfiles(profiles = "development")
public class SecurityTests {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
private HttpMessageConverter mappingJackson2HttpMessageConverter;
private ObjectMapper o = new ObjectMapper();
#Autowired
private FilterChainProxy filterChainProxy;
#Value("${server.servletPath}")
private String servletPath;
#Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(webApplicationContext).addFilter(filterChainProxy).build();
}
#Test
public void testLoginSecurity() throws Exception {
int applicationId = 1;
// Try to access secured api
ResultActions actions = mockMvc.perform(get("/application/" + applicationId))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isForbidden());
//login
String username = "user";
LoginData loginData = new LoginData();
loginData.setPasswordBase64("23j4235jk26=");
loginData.setUsername(username);
actions = mockMvc.perform(post("/login").content(o.writeValueAsString(loginData)).contentType(MediaType.APPLICATION_JSON_VALUE))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.login", Matchers.equalTo(username)))
.andExpect(jsonPath("$.token", notNullValue()))
.andExpect(jsonPath("$.expirationDate", notNullValue()));
Session_model session = getResponseContentAsJavaObject(actions.andReturn().getResponse(), Session_model.class);
Assert.assertNotNull(session);
// Try to access secured api again
actions = mockMvc.perform(get("/application/" + applicationId).header("X-AUTH-TOKEN", session.getToken()))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk());
}
private <T> T getResponseContentAsJavaObject(MockHttpServletResponse response, Class<T> returnType) throws Exception{
return o.readValue(response.getContentAsString(), returnType);
}
#Autowired
void setConverters(HttpMessageConverter<?>[] converters) {
this.mappingJackson2HttpMessageConverter = Arrays.asList(converters).stream().filter(
hmc -> hmc instanceof MappingJackson2HttpMessageConverter).findAny().get();
Assert.assertNotNull("the JSON message converter must not be null",
this.mappingJackson2HttpMessageConverter);
}
}
Maybe I misunderstood something. I hope you can tell me.
Summary
In short you need to map Spring Security to use include the servlet path. Additionally, you need to include the servlet path in your MockMvc requests. To do so you can perform something like:
#Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(webApplicationContext)
// ADD LINE BELOW!!!
.defaultRequest(get("/").servletPath(servletPath))
.addFilter(filterChainProxy)
.build();
}
Detailed Response
Spring Security Matches Based on Context Root
Spring Security's matchers are relative to the application's context root. It is not relative to the servlet path. This is deliberate because it should protect all the servlets (not just Spring MVC). If it were relative to the servlet, consider the following:
servlet1-path/abc -> Only users with role ROLE_ADMIN can access
servlet2-path/abc -> Only users with role ROLE_USER can access
How would you differentiate between these two mappings if Spring Security were relative to the servlet path?
Working in Mock MVC
The reason Spring Security is working in MockMvc is because when you are using MockMvc the servlet path is no longer considered. Your requests are being sent to Spring Security and Spring MVC as though the servlet path is "". To fix this you need to include the servlet path in the request.
#Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(webApplicationContext)
// ADD LINE BELOW!!!
.defaultRequest(get("/").servletPath(servletPath))
.addFilter(filterChainProxy)
.build();
}
greetings everybody
iam using spring security 3 remember me service as follows
<http>
<remember-me/>
....</http>
and i want to perform some logic in the autologin
so i tried to override the AbstractRememberMeServices as follows:
package com.foo;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.RememberMeServices;
public abstract class AbstractRememberMeServices implements RememberMeServices{
#Override
public Authentication autoLogin(HttpServletRequest arg0,
HttpServletResponse arg1) {
System.out.println("Auto Login");
return null;
}
#Override
public void loginSuccess(HttpServletRequest arg0, HttpServletResponse arg1,
Authentication arg2) {
System.out.println("Login Success");
}
}
but the autologin occurs with no action,the user auto login but the print statement is not printed?
what's wrong?
The fact that you have named your class AbstractRememberMeServices does not mean that every other class which was previously extending now extends your com.foo.AbstractRememberMeServices. I don't mean to be impolite, but you need to review your knowledge of Java basics.
Concerning you question, you need to write a custom org.springframework.security.web.authentication.RememberMeService implementation, configure it in Spring and register it using the services-ref attribute:
<security:remember-me services-ref="myRememberMeServices"/>