I am using Spring Security with OAuth2. It's working fine except login success and failure handlers.
Like in spring web security OAuth2 does not have clearly defined success and failure handlers hooks to update DB and set response accordingly.
What filter do I need to extend and what should its position be in the Spring Security filter chain?
Specify successHandler and failureHandler for oauth2login method:
#Configuration
#EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${successUrl}")
private String successUrl;
#Value("${failureUrl}")
private String failureUrl;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login()
.successHandler(successHandler())
.failureHandler(failureHandler());
}
#Bean
SimpleUrlAuthenticationSuccessHandler successHandler() {
return new SimpleUrlAuthenticationSuccessHandler(successUrl);
}
#Bean
SimpleUrlAuthenticationFailureHandler failureHandler() {
return new SimpleUrlAuthenticationFailureHandler(failureUrl);
}
}
Tested for Spring Security 5.0.6
I personally use
#Component
public class MyAuthenticationSuccessListener implements ApplicationListener<AuthenticationSuccessEvent> {
#Override
public void onApplicationEvent(AuthenticationSuccessEvent event) {
System.out.println("Authenticated");
}
}
Additional informations in response can be set by CustomTokenEnhancer
This is a nice tutorial about how to use spring boot with oauth2. Down to the road they show how to configure sso filter by hand:
private Filter ssoFilter(OAuth2Configuration client, String path) {
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(path);
OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
filter.setRestTemplate(template);
filter.setTokenServices(new UserInfoTokenServices(
client.getResource().getUserInfoUri(), client.getClient().getClientId()));
//THIS IS THE PLACE YOU CAN SET THE HANDLER
filter.setAuthenticationSuccessHandler(savedRequestAwareAuthenticationSuccessHandler());
return filter;
}
They didn't provide the line you need, here it is.
The success handler and failure handler are defined in the form-login (if you use Spring's XML). It is not different than any other spring-security definitions:
<security:form-login
login-page="/login/login.htm"
authentication-success-handler-ref="authenticationSuccessHandler"
authentication-failure-url="/login/login.htm?login_error=1" />
and you can find the handler here.
The "failure handler" is pretty similar.
Related
I want to send http request from Ruby code with these values:
http://some_domain.com?key=value&t5052&key=value&key=value
I have this Spring configuration:
#PostMapping(consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, value = "/v1/notification")
public ResponseEntity<String> handleNotifications(#RequestBody MultiValueMap<String, Object> keyValuePairs) {
.....
return new ResponseEntity<>(HttpStatus.OK);
}
Spring convert config:
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(converter -> converter instanceof MappingJackson2XmlHttpMessageConverter);
converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter);
converters.add(new MappingJackson2XmlHttpMessageConverter(
((XmlMapper) createObjectMapper(Jackson2ObjectMapperBuilder.xml()))
.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)));
converters.add(new MappingJackson2HttpMessageConverter(createObjectMapper(Jackson2ObjectMapperBuilder.json())));
}
But I get error:
<h1>Forbidden <span>(403)</span></h1>
<p>CSRF verification failed. Request aborted.</p>
<p>You are seeing this message because this site requires a CSRF cookie when submitting forms. This cookie is required for security reasons, to ensure that your browser is not being hijacked by third parties.</p>
<p>If you have configured your browser to disable cookies, please re-enable them, at least for this site, or for 'same-origin' requests.</p>
Do you know how I can fix this issue? Can I somehow disable this CSRF check in spring?
You can disable CSRF by creating a configuration like this:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
I'm implementing a somewhat simple OAuth2 secured web application according to the guide provided at https://spring.io/guides/tutorials/spring-boot-oauth2/
I need to set a few arbitrary cookies after a successful login to simplify things in my frontend browser application.
Currently I have a working setup that authenticates a user with a Google account utilizing OAuth2.
I intended to use HttpSecurity oauth2Login().successHandler() in my WebSecurityConfigurerAdapter configure() function however I have no ClientRegistrationRepository provided and I don't seem to be able to autowire it.
I couldn't seem to find any standard approach documented anywhere on how to add additional login success logic to the implementation presented in that guide.
This is my main application class, OAuth2 client is configured in the application.yml file.
#SpringBootApplication
#EnableOAuth2Client
public class RestApplication extends WebSecurityConfigurerAdapter {
#Autowired
LogoutSuccessHandler logoutHandler;
#Autowired
OAuth2ClientContext oauth2ClientContext;
public static void main(String[] args) {
SpringApplication.run(RestApplication.class, args);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.antMatcher("/**").authorizeRequests()
.antMatchers("/", "/login**", "/error**", "/webapp/**").permitAll()
.anyRequest().authenticated()
.and().addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class)
.logout().logoutSuccessUrl("/").invalidateHttpSession(true).clearAuthentication(true).deleteCookies("JSESSIONID").logoutSuccessHandler(logoutHandler)
// #formatter:on
}
private Filter ssoFilter() {
OAuth2ClientAuthenticationProcessingFilter authFilter = new OAuth2ClientAuthenticationProcessingFilter(
"/login");
OAuth2RestTemplate oAuthTemplate = new OAuth2RestTemplate(oAuth2ResourceDetails(), oauth2ClientContext);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(oAuth2Resource().getUserInfoUri(),
oAuth2ResourceDetails().getClientId());
authFilter.setRestTemplate(oAuthTemplate);
tokenServices.setRestTemplate(oAuthTemplate);
authFilter.setTokenServices(tokenServices);
return authFilter;
}
#Bean
#ConfigurationProperties("oauth.client")
public AuthorizationCodeResourceDetails oAuth2ResourceDetails() {
return new AuthorizationCodeResourceDetails();
}
#Bean
#ConfigurationProperties("oauth.resource")
public ResourceServerProperties oAuth2Resource() {
return new ResourceServerProperties();
}
#Bean
public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(
OAuth2ClientContextFilter filter) {
FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<OAuth2ClientContextFilter>();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
}
What would be the correct way to add logic that would happen once during a successful authentication, specifically after I have access to the user Principal object.
I've done some further digging in the OAuth2ClientAuthenticationProcessingFilter implementation and found the following possible solution.
It's possible to plug in a custom SessionAuthenticationStrategy which by default is not implemented. The interface documentation states the following:
Allows pluggable support for HttpSession-related behaviour when an authentication occurs.
I've changed the ssoFilter() to the following:
private Filter ssoFilter() {
OAuth2ClientAuthenticationProcessingFilter authFilter = new OAuth2ClientAuthenticationProcessingFilter(
"/login");
authFilter.setSessionAuthenticationStrategy(new SessionAuthenticationStrategy() {
#Override
public void onAuthentication(Authentication authentication, HttpServletRequest request,
HttpServletResponse response) throws SessionAuthenticationException {
LinkedHashMap<String, Object> userDets = (LinkedHashMap<String, Object>) ((OAuth2Authentication) authentication)
.getUserAuthentication().getDetails();
response.addCookie(new Cookie("authenticated", userDets.get("email").toString()));
}
});
OAuth2RestTemplate oAuthTemplate = new OAuth2RestTemplate(oAuth2ResourceDetails(), oauth2ClientContext);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(oAuth2Resource().getUserInfoUri(),
oAuth2ResourceDetails().getClientId());
authFilter.setRestTemplate(oAuthTemplate);
tokenServices.setRestTemplate(oAuthTemplate);
authFilter.setTokenServices(tokenServices);
return authFilter;
}
I have a REST service that uses OAuth2 authentication and that provides an endpoint to request a token with the client_credentials grant type. The application is based on Spring Boot.
So far I figured out I can request a token with something like:
#SpringBootApplication
#EnableOAuth2Client
public class App extends WebSecurityConfigurerAdapter {
#Autowired
OAuth2ClientContext oauth2ClientContext;
//...
#Override
protected void configure(HttpSecurity http) throws Exception {
// Does nothing - to allow unrestricted access
}
#Bean
protected OAuth2RestTemplate myTemplate() {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
details.setAccessTokenUri("http://localhost:8080/oauth/token");
details.setClientId("theClient");
details.setClientSecret("thePassword");
return new OAuth2RestTemplate(details, oauth2ClientContext);
}
}
#RestController
public class TestController {
#Autowired
OAuth2RestTemplate myTemplate;
#RequestMapping("/token")
private String getToken() {
return myTemplate.getAccessToken().getValue();
}
}
And it almost works, but whenever I call the /token endpoint, there's an exception:
org.springframework.security.authentication.InsufficientAuthenticationException: Authentication is required to obtain an access token (anonymous not allowed)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:88) ~[spring-security-oauth2-2.0.9.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221) ~[spring-security-oauth2-2.0.9.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) ~[spring-security-oauth2-2.0.9.RELEASE.jar:na]
...
The exception is thrown here, but I'm not sure how I can make Spring use context authentication other than AnonymousAuthenticationToken. In fact, I don't want any authentication from the client, because anonymous is perfectly okay. How can I achieve this?
I have very basic simple Spring Boot Rest application.
I needed to implement custom authentication in Spring Security: for every REST request I need to check username and password, that are in specific headers of every request ("username" and "password").
So I implemented custom AuthEntryPoint:
#Service
public class CustomAuthEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
String username = httpServletRequest.getHeader("username");
String password = httpServletRequest.getHeader("password");
if (!username.equals("admin") || !password.equals("admin")) {
throw new RuntimeException("", new BadCredentialsException("Wrong password"));
}
}
}
So, I realized, that RequestCacheAwareFilter is caching first request and headers are also stored in cache. So if I make a request with wrong pass and then with right one, I will still get an exception.
So, how could I override the CacheAwareFilter or disable it? Or am I doing something totally wrong?
Use custom WebSecurityConfigurerAdapter to set request cache to NullRequestCache:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestCache()
.requestCache(new NullRequestCache());
}
}
I just made the app stateless like here: How can I use Spring Security without sessions?
And now everything is okay.
I use spring security login. Now I'm trying to add spring social facebook login, but I get many error information.
First, when I try to use the same method like spring social guide, I can't #Autowired private Facebook facebook
I found a solution
#Bean
#Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public Facebook facebook(ConnectionRepository repository) {
Connection<Facebook> connection = repository
.findPrimaryConnection(Facebook.class);
return connection != null ? connection.getApi() : null;
}
Next, I get the error "cannot find bean". I have to add:
#Bean
public ConnectionRepository connectionRepository() {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
if (authentication == null) {
throw new IllegalStateException(
"Unable to get a ConnectionRepository: no user signed in");
}
return usersConnectionRepository().createConnectionRepository(
authentication.getName());
}
#Bean
public ConnectionFactoryLocator connectionFactoryLocator() {
ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry();
registry.addConnectionFactory(new FacebookConnectionFactory(facebookid,
facebookSecure));
return registry;
}
#Bean
public AuthenticationNameUserIdSource authenticationNameUserIdSource(){
return new AuthenticationNameUserIdSource();
}
#Bean
public ConnectController connectController(
ConnectionFactoryLocator connectionFactoryLocator,
ConnectionRepository connectionRepository) {
return new ConnectController(connectionFactoryLocator,
connectionRepository);
}
#Bean
public UsersConnectionRepository usersConnectionRepository() {
return new JdbcUsersConnectionRepository(dataSource,
connectionFactoryLocator(), Encryptors.noOpText());
}
After that, I have other issue java.lang.NoSuchMethodError: org.springframework.social.security.SocialAuthenticationFilter.getFilterProcessesUrl()Ljava/lang/String;
#Bean
public SocialAuthenticationServiceLocator socialAuthenticationServiceLocator() {
SocialAuthenticationServiceRegistry registry = new SocialAuthenticationServiceRegistry();
registry.addConnectionFactory(new FacebookConnectionFactory(facebookid,
facebookSecure));
return registry;
}
#Bean
public SocialAuthenticationFilter socialAuthenticationFilter()
throws Exception {
SocialAuthenticationFilter filter = new SocialAuthenticationFilter(
authenticationManager(), authenticationNameUserIdSource(),
usersConnectionRepository(), socialAuthenticationServiceLocator());
filter.setFilterProcessesUrl("/login");
filter.setSignupUrl("/signup");
filter.setConnectionAddedRedirectUrl("/home");
filter.setPostLoginUrl("/home"); // always open account profile
// page after login
// filter.setRememberMeServices(rememberMeServices());
return filter;
}
but always is the same.
This is my http configuration
http.csrf()
.disable()
.authorizeRequests()
.antMatchers("/home", "/css/**", "/**/*.css*", "/", "/signup",
"/facebook", "/signup.xhtml").permitAll().anyRequest()
.authenticated().and().formLogin().loginPage("/login").loginProcessingUrl("/login/authenticate")
.defaultSuccessUrl("/home").failureUrl("/login")
.permitAll().and().logout().logoutUrl("/logout")
.invalidateHttpSession(true).logoutSuccessUrl("/").and()
.apply(new SpringSocialConfigurer());
And controller
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage() {
return "redirect:/login/authenticate/connect/facebook";
}
I did a whole tutorial. Next, I removed SocialConfigurer implementation and created the same (not #Override, only #Bean) social documentation.
'Normal login '(spring security) works fine, but I can't configure spring social with spring security. I use JSF and .XHTML files.
Maybe someone knows where I make the mistakes?
Thanks for your help.
It looks like Spring Security removed getFilterProcessesUrl() in Spring Security 4.0.0.RC1 (it was marked as deprecated anyways).
It seems that other project filters have not been updated?
Try rolling back to 4.0.0.M2 or use the 3.2 train.
Please notice that spring security 4 will not accept spring social 1.1.0. Please upgrade all spring social dependencies(config, core, security and web) to 1.1.2.RELEASE. You can leave your spring social Facebook to 1.1.0
As hinted in my comment, you have the wrong version of some library. My intelligent guess is that version of Spring Security is wrong. From what I can find, you should use a version in the 3.2.x series (for example 3.2.5) of Spring Security.
Consider using version 1.1.4.
this is solved in spring-social-security 1.1.4.RELEASE (or perhaps some version before).
https://github.com/spring-projects/spring-social