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();
}
}
Related
I've looked at pretty much all of the blogs and SO posts on this topic and I'm not seeing a solution. I have a WebSecurityConfigurerAdapter that looks like this:
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
String issuerUri = "issuer url";
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.authorizeRequests(authorizeRequests ->
authorizeRequests
.antMatchers(HttpMethod.GET, "/test").hasRole("Task.Write")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer
.jwt(jwt ->
jwt.decoder(JwtDecoders.fromIssuerLocation(issuerUri))
)
);
}
I have a controller that looks like this:
#GetMapping(value="/test")
public ApplicationResponse test(#AuthenticationPrincipal Jwt jwt) {
// Map<String, Object> x = jwt.getClaims();
return new ApplicationResponse("ok", "ok");
}
However when I hit this endpoint with Postman with a valid JWT I get a 403 error. I have tried prefixing the role with ROLE_ and Role_ and tried numerous other things but it always is 403.
What's weird too is if I do a permitAll() instead of the authenticate and let the JWT go through. I can look into the JWT object in the controller. I see that the role is there. So why does this WebSecurityConfigurerAdapter always throw a 403 when the JWT is valid and the role is there?
I noticed that the roles are located in the claim in the JWT. Maybe I need to get it from here? I don't see how to get roles from claims in the configure method:
By default, Spring Security converts the items in the scope or scp claim and uses the SCOPE_ prefix. You can change both conventions by defining a custom JwtAuthenticationConverter bean.
To export authorities from a roles scope and use the ROLE_ prefix, you can define the following converter, so that you can use methods like hasRole("Task_Write").
#Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
We have migrated from Basic Authentication to Keycloak method in our project in the production environment. However we would like continue using Basic Authentication, for local development, standalone and demo instalations, which could be triggered by a profile or something like this.
In this project we have REST APIs developed with Java/Spring boot and an AngularJS application which consumes these APIs. We are using Keycloak to protect both AngularJS app and the APIs.
The problem is how to make Spring Security and Keycloak to work "together" in the same application with different profiles. The solution I found so far, was to configure both Spring Security and Keycloak, and made a workaround with properties files, as described below:
application-keycloak.properties
#Unactivate Basic Authentication
security.ignored=/**
application-local-auth.properties
#Unactivate Keycloak
spring.autoconfigure.exclude=org.keycloak.adapters.springboot.KeycloakSpringBootConfiguration
When I wanto to use keycloak, I have to ignore security in order to not have problems and when I want to use basic authentication I have to exclude Keycloak configuration in order to also prevent conflicts.
This is my Security Configuration class:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and()
.authorizeRequests()
.antMatchers("/","/scripts/**","/keycloak/isActive","/keycloak/config","/bower_components/**","/views/**","/fonts/**",
"/views/inventory/dialogs/**", "/services/**","/resources/**","/styles/**", "/info")
.permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
}
And this is my Keycloak Spring Boot configuration:
# Keycloak
keycloak.realm=local
keycloak.realmKey=MIIBIjANBgkqhkiG9wsIIBCgKCAQEAuJYmaWvF3YhifflJhspXOs8RJn74w+eVD8PtpVbu2cYG9OIa49P8SwqVn/kyJQr7kT3OlCq3XMZWBHe+JSzSz7KttKkhfFSfzISdKDKlkPena2H/i3FKlRZIldbeeuQNYdD6nMpzU6QWLwGF1cUAo1M11f2p99QI1FOhVPJSErWsjDsKpWqG+rMMjT1eos0QCNP7krx/yfMdlUyaJCYiDvpOAoec3OWXvDJovEajBNAZMWVXgJF90wAVPRF6szraA2m7K2gG9ozaCNWB0v4Sy6czekbKjqEBPJo45uEmGHd92V//uf/WQG4HSiuv8CTV+b6TQxKtZCpQpqp2DyCLewIDAQAB
keycloak.auth-server-url=http://localhost:8080/auth
keycloak.ssl-required=none
keycloak.resource=App-backend
keycloak.bearer-only=true
keycloak.credentials.secret=a714aede-5af9-4560-8c9d-d655c831772f
keycloak.securityConstraints[0].securityCollections[0].name=Secured API
keycloak.securityConstraints[0].securityCollections[0].authRoles[0]=ROLE_USER
keycloak.securityConstraints[0].securityCollections[0].patterns[0]=/api/*
It is working, however I think it is not an elegant solution. I have tried to implement this using the Keycloak property enable-basic-auth, but I could not understand how it works but it seems that it is just to protect Rest APIs, it does not allow the browser to create a session and use it for all the other requests.
Have someone ever had to implement something like this and can give me some better idea?
I managed to solve this. However, how beautiful my solution is is up for debate.
My use case is that I need to secure most of my endpoints using Keycloak but some (for batch processing) should just use Basic Auth. Configuring both has the downside that Keycloak tries to validate the Authorization Header even if it is Basic Auth so I needed to do three things.
Deactivate all automatic security for my batch route.
Write a custom request filter which secures the batch route.
Manipulate the servlet request object such that the zealous keycloak filter doesn't trip on it.
My security configuration.
#EnableWebSecurity
#EnableResourceServer
public class SecurityConfiguration extends KeycloakWebSecurityConfigureAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
// usual configuration ...
.antMatchers("/api/v1/batch/**").permitAll() // decouple security for this route
.anyRequest().denyAll();
}
}
My custom request filter (needs to run before the spring security filter, thus the ordering annotation):
#Component
#Slf4j
#Order(Ordered.HIGHEST_PRECEDENCE + 2)
public class BasicAuthRequestFilter extends OncePerRequestFilter {
#Value("${batch.user}")
private String user;
#Value("${batch.password}")
private String password;
#Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain
) throws ServletException, IOException {
if (isBatchRequest(request)) {
SimpleHttpFacade facade = new SimpleHttpFacade(request, response);
if (AuthOutcome.AUTHENTICATED.equals(auth(facade))) {
filterChain.doFilter(new AuthentifiedHttpServletRequest(request), response);
}
log.debug("Basic auth failed");
SecurityContextHolder.clearContext();
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unable to authenticate with basic authentication");
return;
}
filterChain.doFilter(request, response);
}
private boolean isBatchRequest(HttpServletRequest request) {
return request.getRequestURI().startsWith("/api/v1/batch/");
}
private AuthOutcome auth(HttpFacade exchange) {
return extractToken(exchange.getRequest().getHeaders(HttpHeaders.AUTHORIZATION))
.map(token -> extractUserPw(token)
.filter(userpw -> verify(userpw.getFirst(), userpw.getSecond()))
.map(userpw -> AuthOutcome.AUTHENTICATED)
.orElse(AuthOutcome.FAILED))
.orElse(AuthOutcome.NOT_ATTEMPTED);
}
private Optional<String> extractToken(List<String> authHeaders) {
return authHeaders == null ? Optional.empty() : authHeaders.stream().map(authHeader -> authHeader.trim().split("\\s+"))
.filter(split -> split.length == 2)
.filter(split -> split[0].equalsIgnoreCase("Basic"))
.map(split -> split[1])
.findFirst();
}
private Optional<Pair<String, String>> extractUserPw(String token) {
try {
String userpw = new String(Base64.decode(token));
String[] parts = userpw.split(":");
if (parts.length == 2) {
return Optional.of(Pair.of(parts[0], parts[1]));
}
} catch (Exception e) {
log.debug("Basic Auth Token formatting error", e);
}
return Optional.empty();
}
private boolean verify(String user, String password) {
return (this.user.equals(user) && this.password.equals(password));
}
}
And finally the wrapped ServletRequest (as you cannot remove Headers from the request):
public class AuthentifiedHttpServletRequest extends HttpServletRequestWrapper {
public AuthentifiedHttpServletRequest(HttpServletRequest request) {
super(request);
}
#Override
public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
return true;
}
#Override
public String getAuthType() {
return "Basic";
}
#Override
public String getHeader(String name) {
if (!HttpHeaders.AUTHORIZATION.equalsIgnoreCase(name)) {
return super.getHeader(name);
}
return null;
}
#Override
public Enumeration<String> getHeaders(String name) {
if (!HttpHeaders.AUTHORIZATION.equalsIgnoreCase(name)) {
return super.getHeaders(name);
}
return Collections.enumeration(Collections.emptyList());
}
#Override
public Enumeration<String> getHeaderNames() {
return Collections.enumeration(EnumerationUtils.toList(super.getHeaderNames())
.stream()
.filter(s -> !HttpHeaders.AUTHORIZATION.equalsIgnoreCase(s))
.collect(Collectors.toList()));
}
#Override
public int getIntHeader(String name) {
if (!HttpHeaders.AUTHORIZATION.equalsIgnoreCase(name)) {
return super.getIntHeader(name);
}
return -1;
}
}
Not quite sure whether this is still relevant or not, but maybe someone will find it helpful.
By default, Keycloak is overwriting plenty of configurations. It's intercepting all Auth request (OAuth2, BasicAuth etc.)
Fortunately, with Keycloak, it's possible to enable authentication both with OAuth2 and BasicAuth in parallel, which I assume is what you want to enable in your dev/localhost environments.
In order to do that, you first need to add the following property to your
application-local-auth.properties:
keycloak.enable-basic-auth=true
This property will enable Basic Auth in your dev environment. However, you also need to enable Basic Auth at your client in Keycloak.
You can accomplish that by connecting to the Keycloak Admin Console on your local Keycloak server and enabling the Direct Access Grant for your client:
Enabling Basic Auth in Keycloak
After that you can authenticate both with Bearer Token and Basic Auth.
I am using spring security along with java config
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/*").hasRole("ADMIN")
.and()
.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class)
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.formLogin()
.successHandler(authenticationSuccessHandler)
.failureHandler(new SimpleUrlAuthenticationFailureHandler());
I am using PostMan for testing my REST services. I get 'csrf token' successfully and I am able to login by using X-CSRF-TOKEN in request header. But after login when i hit post request(I am including same token in request header that i used for login post request) I get the following error message:
HTTP Status 403 - Could not verify the provided CSRF token because your session was not found.
Can any one guide me what I am doing wrong.
According to spring.io:
When should you use CSRF protection? Our recommendation is to use CSRF
protection for any request that could be processed by a browser by
normal users. If you are only creating a service that is used by
non-browser clients, you will likely want to disable CSRF protection.
So to disable it:
#Configuration
public class RestSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
Note: CSRF protection is enabled by default with Java Configuration
try this: #Override protected boolean sameOriginDisabled() { return true;}
#Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
...
// Determines if a CSRF token is required for connecting. This protects against remote
// sites from connecting to the application and being able to read/write data over the
// connection. The default is false (the token is required).
#Override
protected boolean sameOriginDisabled() {
return true;
}
}
source: WebSocket Security: Disable CSRF within WebSockets
Disabling CSRF protection is a bad idea.
Spring will automatically generate a new CSRF token after each request, and you need to include it in all HTTP requests with side-effects (PUT, POST, PATCH, DELETE).
In Postman you can use a test in each request to store the CSRF token in a global, e.g. when using CookieCsrfTokenRepository
pm.globals.set("xsrf-token", postman.getResponseCookie("XSRF-TOKEN").value);
And then include it as a header with key X-XSRF-TOKEN and value {{xsrf-token}}.
Came to same error just with POST methods, was getting 403 Forbidden "Could not verify the provided CSRF token because your session was not found."
After exploring some time found solution by adding #EnableResourceServer annotation to config.
Config looks like that (spring-boot.version -> 1.4.1.RELEASE, spring-security.version -> 4.1.3.RELEASE, spring.version -> 4.3.4.RELEASE)
#Configuration
#EnableWebSecurity
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends ResourceServerConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(inMemoryUserDetailsManager()).passwordEncoder(passwordEncoder());
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.httpBasic();
http.sessionManagement().sessionCreationPolicy(STATELESS);
http.csrf().disable();
http.authorizeRequests().anyRequest()
.permitAll();
}
private InMemoryUserDetailsManager inMemoryUserDetailsManager() throws IOException {
// load custom properties
Properties properties = new Properties();
return new InMemoryUserDetailsManager(properties);
}
private PasswordEncoder passwordEncoder() {
return new TextEncryptorBasedPasswordEncoder(textEncryptor());
}
private TextEncryptor textEncryptor() {
return new OpenSslCompatibleTextEncryptor();
}
}
I get this error message (HTTP Status 403 - Could not verify the provided CSRF token because your session was not found.) when I do a JS fetch AJAX call without using the credentials: "same-origin" option.
Wrong way
fetch(url)
.then(function (response) { return response.json(); })
.then(function (data) { console.log(data); })
Correct way
fetch(url, {
credentials: "same-origin"
})
.then(function (response) { return response.json(); })
.then(function (data) { console.log(data); })
This is an old question but this might help someone. I had the similar issue and this is how I was able to resolve it.
In order for the CSRF to work with the REST API you need to obtain a CSRF token via API before every single call and use that token. Token is different every time and cannot be re-used.
Here is the controller to get the CSRF token:
#RequestMapping(value = "/csrf", method = RequestMethod.GET)
public ResponseEntity<CSRFDTO> getCsrfToken(HttpServletRequest request) {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
return ResponseEntity.ok(CSRFDTO.builder()
.headerName(csrf.getHeaderName())
.token(csrf.getToken())
.build());
}
Additionally, you might consider configuring your Spring app to disable the CSRF for the REST API endpoints. To quote an article I've read somewhere:
I'm very certain that CSRF tokens on a REST endpoint grant zero additional protection. As such, enabling CSRF protection on a REST endpoint just introduces some useless code to your application, and I think it should be skipped.
Hope this helps.
I have solved it by adding the last attribute in my login page,maybe it will do yo a favor.
<%# page language="java" import="java.util.*" pageEncoding="UTF-8" isELIgnored="false"%>
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 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.