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})
I have two applications
a. monolith (developed with java servlet)
b. Spring boot microservice (in development)
I am trying to develop spring cloud api gateway which will sit infront of this two apps and will serve the traffic based on what request it receives. Mostly both apps support POST method and routing in api gateway works fine for b.spring boot microservice but when same request and url I tried on monolith its been observed that spring cloud api gateway sending GET request (which is not allowed ) and getting failed with 404 error. Is there any way to identify what exact request api gateway tried ? OR does api gateway try first GET and then POST ?
I am able to confirm that I am receiving GET request on the POST request that I tried on api gateway by checking the access logs
0:0:0:0:0:0:0:1 - - [14/Jul/2022:12:34:31 -0700] "**GET** /myservice/revers HTTP/1.1" 404 1685 "PostmanRuntime/7.29.0" "0/455"
logs from api gateway after enabling the TRACE
2022-07-15 01:04:30.426 INFO 16016 --- [ctor-http-nio-3] c.t.tes.gwapi.config.LoggingFilter : Incoming request http://localhost:8080/servlet ,is routed uri:http://10.2.1.55:3068/myservice/revers with id: my-route-id
2022-07-15 01:04:31.665 DEBUG 16016 --- [ctor-http-nio-3] r.netty.http.client.HttpClientConnect : [d95e4f2a-1, L:/10.01.57.01:59773 - R:/10.2.1.55:3068] Handler is being applied: {uri=http://10.2.1.55:3068/myservice/revers , method=POST}
2022-07-15 01:04:31.974 DEBUG 16016 --- [ctor-http-nio-3] r.n.http.client.HttpClientOperations : [d95e4f2a-1, L:/10.01.57.01:59773 - R:/10.2.1.55:3068] Received response (auto-read:false) : [Date=Thu, 14 Jul 2022 19:34:31 GMT, Server=Apache, Last-Modified=Wed, 06 Jul 2022 09:15:29 GMT, Accept-Ranges=bytes, Content-Type=text/html, content-length=1685]
2022-07-15 01:04:31.983 DEBUG 16016 --- [ctor-http-nio-3] r.n.http.client.HttpClientOperations : [d95e4f2a-1, L:/10.01.57.01:59773 - R:/10.2.1.55:3068] Received last HTTP packet
2022-07-15 01:04:31.987 INFO 16016 --- [ctor-http-nio-3] c.t.t.gwapi.config.SpringCloudConfig : Response body <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
sample html
Here is sample api config
#Bean
public RouteLocator myRouteSavingRequestBody(RouteLocatorBuilder builder) {
return builder.routes()
.route("my-route-id",
p -> p
.path("/servlet/**") //your own path filter
.filters(f -> f
.modifyResponseBody(String.class, String.class,
(webExchange, originalBody) -> {
if (originalBody != null) {
//log.info("Response body {}", originalBody);
return Mono.just(originalBody);
} else {
return Mono.empty();
}
})
.modifyRequestBody(String.class, String.class,
(webExchange, originalBody) -> {
if (originalBody != null) {
// log.info("Request body {}", originalBody);
return Mono.just(originalBody);
} else {
return Mono.empty();
}
})
.setPath("/myservice/revers")
)
.uri("http://10.2.1.55:3068")
)
.build();
}
I have this controller method:
#GET
#Path("/watch/{groupId}")
public Response watch(#PathParam("groupId") UUID groupId, #RequestHeader("X-Session-ID") String sessionId) {
LOGGER.info("Header passed: " + sessionId);
/* Other stuff */
}
My expectation is that in my watch() method, I'll have the group ID (passed as a URL segment) as a UUID and the session ID (passed as a header) as a String in the corresponding variables. And while the group ID is passed, the header value isn't. This is my log excerpt:
2020-08-27 10:04:34.376 DEBUG 3228 --- [nio-9443-exec-9] o.a.coyote.http11.Http11InputBuffer : Received [GET /watch/8de05f82-2565-4f16-a050-a211f3ce1c1b HTTP/1.1^M
X-Session-ID: 81DC3E39D65236CA09DDF97B3C9C7958075450D0051A9DFD856140530A18C447^M
Host: test.example.com:9443^M
Connection: Keep-Alive^M
Accept-Encoding: gzip^M
User-Agent: okhttp/4.8.1^M
^M
]
2020-08-27 10:04:34.377 DEBUG 3228 --- [nio-9443-exec-9] o.a.c.authenticator.AuthenticatorBase : Security checking request GET /watch/8de05f82-2565-4f16-a050-a211f3ce1c1b
2020-08-27 10:04:34.377 DEBUG 3228 --- [nio-9443-exec-9] org.apache.catalina.realm.RealmBase : No applicable constraints defined
2020-08-27 10:04:34.377 DEBUG 3228 --- [nio-9443-exec-9] o.a.c.authenticator.AuthenticatorBase : Not subject to any constraint
2020-08-27 10:04:34.385 INFO 3228 --- [nio-9443-exec-9] i.r.frontend.watcher.WatchController : Header passed:
2020-08-27 10:04:34.392 DEBUG 3228 --- [nio-9443-exec-9] stomAnnotationTransactionAttributeSource : Adding transactional method 'findAll' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
As you can see, the log entry for the session ID is not showing anything.
I am out of ideas as to why this is happening. Can someone help?
OK, I have figured out the problem here.
#GET is javax.ws.rs.GET.
#Path is javax.ws.rs.Path.
#PathParam is javax.ws.rs.PathParam.
But #RequestHeader is org.springframework.web.bind.annotation.RequestHeader.
That is, I was mixing Spring's REST and JAX-RS's. I replaced #RequestHeader (Spring) with #HeaderParam (JAX-RS) and now I get the HTTP header's value in my code.
Which is better, which one should I use etc. I'll figure out later. Right now the problem is solved.
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'm building spring mvc application with spring security.
This is my url :
http://localhost:8080/inbalUI/login
And I'm getting to my controller :
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage() {
if (isCurrentAuthenticationAnonymous()) {
return "login";
} else {
return "login";
// return "redirect:/list";
}
}
This is the log :
20:02:10.823 [http-nio-8080-exec-10] DEBUG org.springframework.web.servlet.view.JstlView - Forwarding to resource [/WEB-INF/views/login.jsp] in InternalResourceView 'login'
20:02:10.825 [http-nio-8080-exec-10] DEBUG org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
20:02:10.825 [http-nio-8080-exec-10] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request
20:02:10.825 [http-nio-8080-exec-10] DEBUG org.springframework.security.web.access.ExceptionTranslationFilter - Chain processed normally
20:02:10.825 [http-nio-8080-exec-10] DEBUG org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
This is the login.jsp deploying path on tomcat :
C:\me\apache\apache-tomcat-8.0.36\webapps\inbalUI\WEB-INF\classes\WEB-INF\views
But I'm getting HTTP Status 404:
HTTP Status 404 - /inbalUI/WEB-INF/views/login.jsp
type Status report
message /inbalUI/WEB-INF/views/login.jsp
description The requested resource is not available.
Apache Tomcat/8.0.36
Any idea why ?
It seems to me that you use the wrong folder for views.
According to this article https://vitalflux.com/web-application-folder-structure-spring-mvc-web-projects/ this folder should be at:
src/WEB-INF/views/login.jsp
and you probably put it under
src/resources/WEB-INF/views/login.jsp
Which results in WEB-INF/views/login.jsp being put into the classpath (classes/WEB-INF/views/login.jsp)