I have created this Spring Boot backend for a university assignment, which requires authorization/authentication. I have made the api work fine standalone , and the calls go through with correct authorization/authentication through Postman requests. But when i try to call the api from a react app, it always returns unauthanticaded 401.
I removed the security completely from the api, just to see if it would work, it does , the api calls return the results as expected (without Spring Security). But when i re-add Spring security only the Postman requests work.
Javascript Fetch requests don't work (Returning 401)
var myHeaders = new Headers();
myHeaders.append("Authorization", "Basic " + btoa("root:root"));
const res = await fetch("http://localhost:7979/api/users", myHeaders)
console.log(res);
const data = await res.json();
console.log(data);
Console Output:
and wget/curl requests don't work with both outputing Connection Refused
Successful Postman request:
Here is the security config from the spring boot backend:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig
{
#Resource
private DataSource dataSource;
#Autowired
private UserDetailsService userDetailsService;
#Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception
{
http.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.POST , "/api/users").permitAll()
.requestMatchers( "/api/authority").hasAuthority(Constants.ADMIN)
.anyRequest().authenticated()
)
.formLogin().permitAll()
.and().logout().permitAll()
.and().userDetailsService(userDetailsService)
.cors()
.and().csrf().disable()
.httpBasic();
http.headers().frameOptions().sameOrigin();
return http.build();
}
#Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
}
the spring backend is run on localhost:7979
and the react app is run on localhost:3000
also, maybe important, i am on windows and run the React app on Windows Subsystem for Linux but the spring app but the default runner of Intellij.
I'm quite stumped and i can't figure out how to debug Spring Security, everyone just says enable logging which i have done but the Spring Security Logging offers 0 useful info (unless i don't understand what I'm seeing)
Update:
So wsl is a bit of a problem, i have this script on .zshrc on wsl that get the ip of the windows host (i don't know how it works, a person here told me to add it and it worked)
script:
export winhost=$(cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }')
if [ ! -n "$(grep -P "[[:space:]]winhost" /etc/hosts)" ]; then
printf "%s\t%s\n" "$winhost" "winhost" | sudo tee -a "/etc/hosts"
fi
so now if i use the winhost instead of localhost wget/curl works fine
Successful wget request:
Successful curl request:
I tried doing the same on the React app but it came up with a new error
I also tried running the React app on windows instead of wsl , but i had the same result:
Update Update:
I found a way to activate logging on spring security, here are the log for a successful request:
2023-01-26T02:00:33.435+02:00 DEBUG 17932 --- [nio-7979-exec-9] o.s.security.web.FilterChainProxy : Securing GET /api/users
Hibernate:
select
u1_0.`id`,
u1_0.`AFM`,
u1_0.`address`,
u1_0.`email`,
u1_0.`enabled`,
u1_0.`password`,
u1_0.`phone_number`,
u1_0.`username`
from
`users` u1_0
where
u1_0.`username`=?
Hibernate:
select
a1_0.`user_id`,
a1_1.`id`,
a1_1.`authority`
from
`user_authorities` a1_0
join
`authorities` a1_1
on a1_1.`id`=a1_0.`authority_id`
where
a1_0.`user_id`=?
Hibernate:
select
u1_0.`authority_id`,
u1_1.`id`,
u1_1.`AFM`,
u1_1.`address`,
u1_1.`email`,
u1_1.`enabled`,
u1_1.`password`,
u1_1.`phone_number`,
u1_1.`username`
from
`user_authorities` u1_0
join
`users` u1_1
on u1_1.`id`=u1_0.`user_id`
where
u1_0.`authority_id`=?
2023-01-26T02:00:33.508+02:00 DEBUG 17932 --- [nio-7979-exec-9] o.s.s.a.dao.DaoAuthenticationProvider : Authenticated user
2023-01-26T02:00:33.508+02:00 DEBUG 17932 --- [nio-7979-exec-9] o.s.s.w.a.www.BasicAuthenticationFilter : Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=gr.hua.dit.it22023_it22026.models.SecurityUserDetails#2e536336, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=172.20.196.70, SessionId=null], Granted Authorities=[ADMIN]]
2023-01-26T02:00:33.509+02:00 DEBUG 17932 --- [nio-7979-exec-9] o.s.security.web.FilterChainProxy : Secured GET /api/users
Hibernate:
select
u1_0.`id`,
u1_0.`AFM`,
u1_0.`address`,
u1_0.`email`,
u1_0.`enabled`,
u1_0.`password`,
u1_0.`phone_number`,
u1_0.`username`
from
`users` u1_0
Hibernate:
select
a1_0.`user_id`,
a1_1.`id`,
a1_1.`authority`
from
`user_authorities` a1_0
join
`authorities` a1_1
on a1_1.`id`=a1_0.`authority_id`
where
a1_0.`user_id`=?
Hibernate:
select
u1_0.`authority_id`,
u1_1.`id`,
u1_1.`AFM`,
u1_1.`address`,
u1_1.`email`,
u1_1.`enabled`,
u1_1.`password`,
u1_1.`phone_number`,
u1_1.`username`
from
`user_authorities` u1_0
join
`users` u1_1
on u1_1.`id`=u1_0.`user_id`
where
u1_0.`authority_id`=?
Hibernate:
select
a1_0.`user_id`,
a1_1.`id`,
a1_1.`authority`
from
`user_authorities` a1_0
join
`authorities` a1_1
on a1_1.`id`=a1_0.`authority_id`
where
a1_0.`user_id`=?
Hibernate:
select
a1_0.`user_id`,
a1_1.`id`,
a1_1.`authority`
from
`user_authorities` a1_0
join
`authorities` a1_1
on a1_1.`id`=a1_0.`authority_id`
where
a1_0.`user_id`=?
Hibernate:
select
u1_0.`authority_id`,
u1_1.`id`,
u1_1.`AFM`,
u1_1.`address`,
u1_1.`email`,
u1_1.`enabled`,
u1_1.`password`,
u1_1.`phone_number`,
u1_1.`username`
from
`user_authorities` u1_0
join
`users` u1_1
on u1_1.`id`=u1_0.`user_id`
where
u1_0.`authority_id`=?
And here are the logs for a failed request:
2023-01-26T02:02:23.041+02:00 DEBUG 17932 --- [nio-7979-exec-1] o.s.security.web.FilterChainProxy : Securing GET /api/users
2023-01-26T02:02:23.042+02:00 DEBUG 17932 --- [nio-7979-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2023-01-26T02:02:23.042+02:00 DEBUG 17932 --- [nio-7979-exec-1] s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]
2023-01-26T02:02:23.042+02:00 DEBUG 17932 --- [nio-7979-exec-1] s.w.a.DelegatingAuthenticationEntryPoint : No match found. Using default entry point org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint#456e1a23
2023-01-26T02:02:23.042+02:00 DEBUG 17932 --- [nio-7979-exec-1] o.s.security.web.FilterChainProxy : Securing GET /error
2023-01-26T02:02:23.042+02:00 DEBUG 17932 --- [nio-7979-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2023-01-26T02:02:23.042+02:00 DEBUG 17932 --- [nio-7979-exec-1] s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]
2023-01-26T02:02:23.042+02:00 DEBUG 17932 --- [nio-7979-exec-1] s.w.a.DelegatingAuthenticationEntryPoint : No match found. Using default entry point org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint#456e1a23
I don't understand the fail request logs at all, can any spring boot security expert help?
issue was in javascript
const res = await fetch("http://localhost:7979/api/users", headers);
should have been
const res = await fetch("http://localhost:7979/api/users", {headers:headers})
My Java Spring Authentication and Authorization Server is working.
I want to solve the errors in the requests I send from a client according to the OAuth2 flow. But none of the errors I get are detailed. For example, this request sent to /oauth2/authorize:
https://MY_DOMAIN/authorize?
response_type=code&
client_id=MY_CLIENT_ID&
redirect_uri=MY_CALLBACK_URL&
scope=SCOPE&
state=STATE
Gives the following error after I enter credentials:
My goal is not to resolve the error here, but there are different errors I get like this and I need to see a more informative error message so I can debug them all.
The console also has a non-informative output that goes like this:
...
2022-08-15 15:52:33.739 DEBUG 50827 --- [nio-9000-exec-4] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2022-08-15 15:52:33.739 DEBUG 50827 --- [nio-9000-exec-4] o.a.c.c.C.[Tomcat].[localhost] : Processing ErrorPage[errorCode=0, location=/error]
2022-08-15 15:52:33.739 DEBUG 50827 --- [nio-9000-exec-4] o.s.security.web.FilterChainProxy : Securing GET /error?protocol=oauth2&response_type=code&access_type&client_id=articles-client&redirect_uri=http%3A%2F%2F127.0.0.1%3A3000%2Flms%2Flogin&scope=openid%20articles.read&state=XqqDv6wx6O&code_challenge_method=plain&code_challenge=e964fe0c4b609ef3cf29658efc6077e7feb591f78c458c2092aa56c9
...
im trying to to read a value from Google-Cloud storage from my Spring application. I use the Spring Cloud GCP extension to work with Google Cloud Storage.
My Pom.xml for the gcp dependency:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-starter-storage</artifactId>
<version>1.1.2.RELEASE</version>
</dependency>
When i try to read a file from my rest-endpoint i get the exception(at the end of my answer) that somehow that my token could not be refreshed ? Where i can set my clientId or is there something else going on ? I used the code from the sample application which is provided by pivotal and google.
#RestController
public class GCloudStorageController {
#Value("gs://test_files_test/test.txt")
private Resource gcsFile;
#RequestMapping(value = "/cloud", method = RequestMethod.GET)
public String readGcsFile() throws IOException {
return StreamUtils.copyToString(
this.gcsFile.getInputStream(),
Charset.defaultCharset()) + "\n";
}
#RequestMapping(value = "/cloud", method = RequestMethod.POST)
String writeGcs(#RequestBody String data) throws IOException {
try (OutputStream os = ((WritableResource) this.gcsFile).getOutputStream()) {
os.write(data.getBytes());
}
return "file was updated\n";
}
}
2019-08-20 20:27:02.555 DEBUG 12348 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Failed to complete request: com.google.cloud.storage.StorageException: 400 Bad Request
{
"error": "invalid_grant",
"error_description": "Bad Request"
}
2019-08-20 20:27:02.556 DEBUG 12348 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#24f1dc0f
2019-08-20 20:27:02.557 DEBUG 12348 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2019-08-20 20:27:02.557 DEBUG 12348 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2019-08-20 20:27:02.563 ERROR 12348 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.google.cloud.storage.StorageException: 400 Bad Request
{
"error": "invalid_grant",
"error_description": "Bad Request"
}] with root cause
com.google.api.client.http.HttpResponseException: 400 Bad Request
{
"error": "invalid_grant",
"error_description": "Bad Request"
}
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1094) ~[google-http-client-1.30.1.jar:na]
at com.google.auth.oauth2.UserCredentials.refreshAccessToken(UserCredentials.java:193) ~[google-auth-library-oauth2-http-0.16.1.jar:na]
at com.google.auth.oauth2.OAuth2Credentials.refresh(OAuth2Credentials.java:165) ~[google-auth-library-oauth2-http-0.16.1.jar:na]
at com.google.auth.oauth2.OAuth2Credentials.getRequestMetadata(OAuth2Credentials.java:151) ~[google-auth-library-oauth2-http-0.16.1.jar:na]
at com.google.auth.http.HttpCredentialsAdapter.initialize(HttpCredentialsAdapter.java:96) ~[google-auth-library-oauth2-http-0.16.1.jar:na]
at com.google.cloud.http.HttpTransportOptions$1.initialize(HttpTransportOptions.java:159) ~[google-cloud-core-http-1.79.0.jar:1.79.0]
at com.google.cloud.http.CensusHttpModule$CensusHttpRequestInitializer.initialize(CensusHttpModule.java:109) ~[google-cloud-core-http-1.79.0.jar:1.79.0]
at com.google.api.client.http.HttpRequestFactory.buildRequest(HttpRequestFactory.java:88) ~[google-http-client-1.30.1.jar:na]
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.buildHttpRequest(AbstractGoogleClientRequest.java:430) ~[google-api-client-1.30.1.jar:1.30.1]
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:549) ~[google-api-client-1.30.1.jar:1.30.1]
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:482) ~[google-api-client-1.30.1.jar:1.30.1]
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:599) ~[google-api-client-1.30.1.jar:1.30.1]
at com.google.cloud.storage.spi.v1.HttpStorageRpc.get(HttpStorageRpc.java:433) ~[google-cloud-storage-1.79.0.jar:1.79.0]
at com.google.cloud.storage.StorageImpl$5.call(StorageImpl.java:240) ~[google-cloud-storage-1.79.0.jar:1.79.0]
at com.google.cloud.storage.StorageImpl$5.call(StorageImpl.java:237) ~[google-cloud-storage-1.79.0.jar:1.79.0]
at com.google.api.gax.retrying.DirectRetryingExecutor.submit(DirectRetryingExecutor.java:105) ~[gax-1.46.1.jar:1.46.1]
at com.google.cloud.RetryHelper.run(RetryHelper.java:76) ~[google-cloud-core-1.79.0.jar:1.79.0]
at com.google.cloud.RetryHelper.runWithRetries(RetryHelper.java:50) ~[google-cloud-core-1.79.0.jar:1.79.0]
at com.google.cloud.storage.StorageImpl.get(StorageImpl.java:236) ~[google-cloud-storage-1.79.0.jar:1.79.0]
at com.google.cloud.storage.StorageImpl.get(StorageImpl.java:254) ~[google-cloud-storage-1.79.0.jar:1.79.0]
at org.springframework.cloud.gcp.storage.GoogleStorageResource.getBlob(GoogleStorageResource.java:165) ~[spring-cloud-gcp-storage-1.1.2.RELEASE.jar:1.1.2.RELEASE]
at
Thank you Joe. The resource to implement 'Spring Cloud GCP Core' was never mentioned in the tutorials or i overlooked it.
Somehow another account was connected in my Google Cloud SDK on my console. So i used
gcloud auth application-default login
and logged in the right account. Now it works. Thank you.
This looks like an issue regarding authentication. Did you follow the generic 'Spring Cloud GCP Core' [1] configuration?
Check your application.properties [2] (or other configuration) and make sure it contains at least the following properties:
spring.cloud.gcp.datastore.project-id=XXX
spring.cloud.gcp.datastore.credentials.location=YYY
or choose an other method shown in [1].
[1] https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/1.1.2.RELEASE/single/spring-cloud-gcp.html#spring-cloud-gcp-core
[2] https://github.com/spring-cloud/spring-cloud-gcp/blob/master/spring-cloud-gcp-samples/spring-cloud-gcp-data-datastore-sample/src/main/resources/application.properties
I am having a issue related to authentication where I am getting logged out of my app when trying to GET from an endpoint that was created from a POST (HTTTP STATUS 201) I see the following errors. The backend is seeing the user as anonymousUser while I am logged in but works fine for another endpoint in the same file :
2018-10-22 11:33:35.251 DEBUG 2124 --- [ XNIO-8 task-23]
c.c.link.aop.logging.LoggingAspect : Enter:
org.springframework.boot.actuate.audit.AuditEventRepository.add() with
argument[s] = [AuditEvent [timestamp=Mon Oct 22 11:33:35 PDT 2018,
principal=anonymousUser, type=AUTHORIZATION_FAILURE,
data={details=org.springframework.security.web.authentication.WebAuthenticationDetails#fffed504:
RemoteIpAddress: 127.0.0.1; SessionId:
_5aP-S8x27gGSIjtbkR6EwrWOD9Yybnd-4M3z0ol, type=org.springframework.security.access.AccessDeniedException,
message=Access is denied}]] 2018-10-22 11:33:35.258 DEBUG 2124 --- [
XNIO-8 task-23] c.c.link.aop.logging.LoggingAspect : Exit:
org.springframework.boot.actuate.audit.AuditEventRepository.add() with
result = null 2018-10-22 11:33:35.343 WARN 2124 --- [ XNIO-8 task-23]
o.z.p.spring.web.advice.AdviceTrait : Unauthorized: Full
authentication is required to access this resource
The same resource (DeliveryResource.java) has an endpoint that works fine
the only difference between both end points is one is /getList (this one is working fine) VS. /getPackingListReport/{number} (this one fails authentication).
The API definitions are shown below:
#GetMapping("/getPackingListReport/{number}")
#Secured(AuthoritiesConstants.USER)
#Timed
public ArrayList<WebOrder> getPackingListReportFromDB(#PathVariable("number") long packingListNbr)
vs.
#GetMapping("/getList")
#Secured(AuthoritiesConstants.USER)
#Timed
public WebOrder getList(#RequestParam(value = "custid") long custid,#RequestParam(value = "pId") long pId) {
I found the problem.
I was using the following for the "/getList" mapping:
// call dao to insert to DB the list and then call the stored proc
URI location = ServletUriComponentsBuilder.fromCurrentContextPath().path("/api/rest/getPackingListReport/{packingListNumber}").buildAndExpand(packingListNumber).toUri();
return ResponseEntity.created(location).build();
to return a created resource URI (HTTP STATUS 201) after a POST. That path contained the incorrect URI starting with , "http://localhost" when jHipster is currently default set to only handle and look for "/api" or in my case i added "/rest/api/*" on the Angular5 side.
I fixed the issue by filtering out any characters before "/api/" after receiving the URL.
I have created a jhipster gateway and micro-service project along with a UAA server. It is working fine in my local environment. However, when I try to deploy it to a server and run it, I get an error similar to the below when I try to log in from the server-deployed gateway project:
Not a valid Domain name
Error :
2018-01-25 12:02:42.242 DEBUG 10852 --- [ XNIO-2 task-6] w.i.s.o.OAuth2TokenEndpointClientAdapter : contacting OAuth2 token endpoint to login user: admin
2018-01-25 12:02:42.419 ERROR 10852 --- [ XNIO-2 task-6] c.w.i.s.o.OAuth2AuthenticationService : failed to get OAuth2 tokens from UAA
java.lang.IllegalArgumentException: Not a valid domain name: '192.168.0.202'
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:210)
at com.google.common.net.InternetDomainName.<init>(InternetDomainName.java:155)
at com.google.common.net.InternetDomainName.from(InternetDomainName.java:216)
at com.wdsi.iloads.security.oauth2.OAuth2CookieHelper.getCookieDomain(OAuth2CookieHelper.java:296)
at com.wdsi.iloads.security.oauth2.OAuth2CookieHelper.createCookies(OAuth2CookieHelper.java:109)
at com.wdsi.iloads.security.oauth2.OAuth2AuthenticationService.authenticate(OAuth2AuthenticationService.java:70)
at com.wdsi.iloads.web.rest.AuthResource.authenticate(AuthResource.java:51)
at com.wdsi.iloads.web.rest.AuthResource$$FastClassBySpringCGLIB$$fdfdf7ca.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
at com.wdsi.iloads.aop.logging.LoggingAspect.logAround(LoggingAspect.java:85)
Any help is appreciated.
This was fixed recently in JHipster's generator by this pull request.
To fix it locally in your project, edit OAuth2CookieHelper.java (in the security/oauth2/ package of your Java folder). Add an extra check for IP addresses before parsing the address as a domain name.
// add this import
import com.google.common.net.InetAddresses;
// add this if-statement surrounding the domain name parsing
// if it isn't an IP address
if (!InetAddresses.isInetAddress(domain)) {
// strip off subdomains, leaving the top level domain only
InternetDomainName domainName = InternetDomainName.from(domain);
if (domainName.isUnderPublicSuffix() && !domainName.isTopPrivateDomain()) {
// preserve leading dot
return "." + domainName.topPrivateDomain().toString();
}
}