Validate an authToken - java

Got a websocket - see the authToken in the Cookie, in Java Spring how do you validate this authToken? I understand this authToken is passed down from the http layer to the websocket so I'm trying to validate that the websocket is being opened by our app and not by some other source.
Headers for Websocket:
GET ws://localhost:9999/somePath/websocket HTTP/1.1
Host: localhost:9999
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36
Upgrade: websocket
Origin: http://localhost
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: authToken=elFKMk5TckR0ZUNvdnZySUJxc2ZMdz09OklEZENrRFRySkp0U0ltVFdKU1RIZVE9PQ
Sec-WebSocket-Key: e//VDAjHSRjE810tCbIEyw==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Protocol: v10.stomp, v11.stomp, v12.stomp
I would like to validate that authToken in the HttpHandshakeInterceptor.beforeHandshake
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer
registry.addEndpoint(stompEndPoint).addInterceptors(new HttpHandshakeInterceptor()).setAllowedOrigins("*").withSockJS();
public class HttpHandshakeInterceptor implements HandshakeInterceptor
Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession();
attributes.put("sessionId", session.getId());
// validate token logic
}
return true;
}

Related

Spring Security Requests are 403

I am currently working on an application with spring, and I currently face the problem that all requests I do return the error 403 - Forbidden. It is not only [post,put,patch,delete], but also get. Also, I have csrf already disabled.
Here my SecurityConfig:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.anonymous().and()
.authorizeRequests()
.antMatchers("/signin").permitAll()
.antMatchers("/signup").permitAll()
.antMatchers("/rss/feed").permitAll()
.antMatchers("/article/{guid}").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint).and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.csrf().disable().cors();
}
And here one of the endpoints which is not working properly:
#PreAuthorize("hasAnyRole('ADMIN', 'PUBLISHER', 'USER')")
#GetMapping("/users/current")
public User getCurrent(#RequestHeader Map<String, String> headers){
String token = headers.get("Authorization");
System.out.println("Current user request");
return userAuthService.getUserByUsername(jwtUtil.getUser(token).getUsername());
}
And yes, I know that csrf().disable() is dangerous, I disabled it for now to see if it is some problem with csrf.
Here my JwtAuthenticationFilter:
#Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private JwtUtil jwtUtil;
#Autowired
private UserAuthService userAuthService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String header = request.getHeader("Authorization");
if(header == null){
throw new NullPointerException("No headers");
}
if (!header.startsWith("Bearer")) {
throw new JwtTokenMissingException("No JWT token found in the request headers");
}
String token = header.substring(7);
// Optional - verification
jwtUtil.validateToken(token);
UserVo userVo = jwtUtil.getUser(token);
UserDetails userDetails = userAuthService.loadUserByUsername(userVo.getUsername());
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
if (SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
filterChain.doFilter(request, response);
}
}
Edit: I enabled Security logging and got the following error on Spring console:
2022-04-04 18:22:26.611 DEBUG 9804 --- [nio-8080-exec-2] o.s.s.a.i.a.MethodSecurityInterceptor : Failed to authorize ReflectiveMethodInvocation: public at.brigot.kainblog.pojos.User at.brigot.kainblog.controller.AuthController.getCurrent(java.util.Map); target is of class [at.brigot.kainblog.controller.AuthController] with attributes [[authorize: 'hasAnyRole('ADMIN', 'PUBLISHER', 'USER')', filter: 'null', filterTarget: 'null']]
Also if needed, here the full request info I got from spring:
************************************************************
Request received for GET '/users/current':
org.apache.catalina.connector.RequestFacade#1d49a6ca
servletPath:/users/current
pathInfo:null
headers:
host: localhost:8080
connection: keep-alive
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98", "Opera GX";v="84"
accept: application/json, text/plain, */*
authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJnb3RwZWQxNyIsInJvbGVzIjpbIkFETUlOIiwiVVNFUiJdLCJpYXQiOjE2NDkwODkyMDcsImV4cCI6MTY0OTA4OTM4N30.b5vg-azO433Ozk8GoiakQC-T2ULdFVsde6MrJhW8XpIhA5k5AtA_Q6i0vuCGATQV8RwteMzBc86CzKmuQ7kuYA
sec-ch-ua-mobile: ?0
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36 OPR/84.0.4316.52
sec-ch-ua-platform: "Windows"
origin: http://localhost:3000
sec-fetch-site: same-site
sec-fetch-mode: cors
sec-fetch-dest: empty
referer: http://localhost:3000/
accept-encoding: gzip, deflate, br
accept-language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CorsFilter
LogoutFilter
JwtAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
************************************************************

Why CorsConfigurationSource's is ignored, no Access-Control-Allow-Origin is present?

My goal is to add Access-Control-Allow-Origins in the header.
The Spring Security Reference's clearly said to
Set a POJO as #EnableWebSecurity's bean.
Override a configure(HttpSecurity http) method.
Create a CorsConfigurationSource's bean.
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) {
http
.cors(withDefaults())
.httpBasic(withDefaults());
}
#Bean
protected CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
#RestController
public class AccountController {
#GetMapping("/ping")
public String getPing() {
return "PONG";
}
}
What I did:
Run a Spring Boot's application with the above configuration
Send a Fetch request to http://localhost:8080/ping from a React client.
The actual result:
The response does not include Access-Control-Allow-Origin header, I think it should be in the OPTIONS request.
The expected result:
A success response.
I am confused... I did a curl and the Access-Control-Allow-Origin is present. I think there is something wrong with the client side handling the HttpBasic.
curl -v -H "Access-Control-Request-Method: POST" -H "Origin: http://localhost:3000" -X OPTIONS http://localhost:8080/ping
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> OPTIONS /ping HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.55.1
> Accept: */*
> Access-Control-Request-Method: POST
> Origin: http://localhost:3000
>
< HTTP/1.1 200
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Access-Control-Allow-Origin: http://localhost:3000
< Access-Control-Allow-Methods: GET,POST
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Length: 0
< Date: Fri, 26 Feb 2021 13:37:15 GMT
<
* Connection #0 to host localhost left intact

Trouble connecting angular frontEnd with javaBackend?

I am trying to call the API in Backend but I have some error that I have not an idea from what is causing by.
The problem started after I config the spring security in the backend.
The call should activate Preflighted requests OPTION
In my backend file, I have
#Configuration
#EnableWebSecurity
public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
.anyRequest().authenticated()
.and()
// .formLogin().and()
.httpBasic();
}
}
and in the frontend, I have this part of the code.
executeHelloWorldServiceWithPathVariable(name) {
const basicAuthHeaderString = this.createBasicAuthenticationHttpHeader();
const headers = new HttpHeaders({
Authorization: basicAuthHeaderString
});
return this.http.get<HelloWorldBean>(`http://localhost:8080/hello-world/path-variable/${name}`,
{headers});
}
createBasicAuthenticationHttpHeader() {
const username = 'start';
const password = 'end';
const basicAuthHeaderString = 'Basic ' + window.btoa(username + ':' + password);
return basicAuthHeaderString;
}
In the backend, I have already include
#CrossOrigin(origins = "http://localhost:4200")
but still, I am not able to call this API
in the console, I should get something like an OPTION method but in fact, I get those:
General
Request URL: http://localhost:8080/hello-world/path-variable/start
Referrer Policy: no-referrer-when-downgrade
Response Header
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: keep-alive
Content-Length: 0
Date: Tue, 28 Jan 2020 11:11:49 GMT
Expires: 0
Keep-Alive: timeout=60
Pragma: no-cache
WWW-Authenticate: Basic realm="Realm"
WWW-Authenticate: Basic realm="Realm"
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
request head
Accept: application/json, text/plain, /
Accept-Encoding: gzip, deflate, br
Accept-Language: en,cs;q=0.9,en-US;q=0.8
Authorization: Basicc3RhcnQ6ZWVuZA==
Connection: keep-alive
Host: localhost:8080
Origin: http://localhost:4200
Referer: http://localhost:4200/welcome/start
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
and in console, I see this error
Try to add cors policy in your security configuration:
#Configuration
#EnableWebSecurity
public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.cors();
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
.anyRequest().authenticated()
.and()
// .formLogin().and()
.httpBasic();
}
}
to the class SpringSecurityConfigurationBasicAuth try to add this method
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}

Unauthorized request, 401, using Spring if I open a modal or I send an ajax request

I have a problem with Keycloak and Spring Boot..
I developed a web-app with some modal and ajax request, sometimes, and I don't know why, I receive status 401 if I click on an href to open a modal, or if I submit a form via ajax...
I don't see any error log server-side, but I checked the request and I have WWW-Authenticate: Bearer realm="Unknown". I think it is weird.
This is the entire request:
1. Request URL:
MyUrl
2. Request Method:
GET
3. Status Code:
401 Unauthorized
4. Remote Address:
MyIp
5. Referrer Policy:
no-referrer-when-downgrade
Response Headers
1. Cache-Control:
no-cache, no-store, max-age=0, must-revalidate
2. Connection:
Keep-Alive
3. Content-Language:
it
4. Content-Length:
302
5. Content-Type:
text/html;charset=ISO-8859-1
6. Date:
Tue, 08 May 2018 07:32:59 GMT
7. Expires:
0
8. Keep-Alive:
timeout=5, max=99
9. Pragma:
no-cache
10. Server:
Apache/2.4.18 (Ubuntu)
11. WWW-Authenticate:
Bearer realm="Unknown"
12. X-Content-Type-Options:
nosniff
13. X-Frame-Options:
DENY
14. X-XSS-Protection:
1; mode=block
Request Header
1. Accept:
text/html, */*; q=0.01
2. Accept-Encoding:
gzip, deflate
3. Accept-Language:
it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7
4. Connection:
keep-alive
5. Cookie:
JSESSIONID=1F97669EEE8A347CAE145C8D25146512.Tomcat1; _ga=GA1.2.230482432.1486023475
6. DNT:
1
7. Host:
MyHost
8. Referer:
MyUrl
9. User-Agent:
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36
10. X-Requested-With:
XMLHttpRequest
Query String Parameters
.....
This is my config about keycloak and Spring:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
#KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
public KeycloakClientRequestFactory keycloakClientRequestFactory;
public SecurityConfig() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.httpBasic()
.disable();
http
.authorizeRequests()
.anyRequest().hasAuthority("1086")
.and()
.logout()
.logoutUrl("/logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
.permitAll()
.logoutSuccessUrl(URL)
.invalidateHttpSession(true);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public KeycloakRestTemplate keycloakRestTemplate() {
return new KeycloakRestTemplate(keycloakClientRequestFactory);
}
#Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
#Bean
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(KeycloakAuthenticationProcessingFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
#Bean
public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(KeycloakPreAuthActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
#Bean
public FilterRegistrationBean keycloakAuthenticatedActionsFilterBean(KeycloakAuthenticatedActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
#Bean
public FilterRegistrationBean keycloakSecurityContextRequestFilterBean( KeycloakSecurityContextRequestFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**", "/webjars/**");
}
I can continue with the request, if I reload the page, and then I re-click on the href... But why I have to reload the page? and why sometimes?

How Spring MVC make HttpServletRequest field threadsafe?

Code:
package com.test.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;
#RestController
#RequestMapping("/")
public class Controller {
private HttpServletRequest request;
public HttpServletRequest getRequest() {
return request;
}
#Autowired
public void setRequest(HttpServletRequest request) {
this.request = request;
}
#RequestMapping("safe-read")
public void threadSafeRead() throws InterruptedException {
System.out.println(request.getHeader("user-agent"));
Thread.sleep(TimeUnit.MILLISECONDS.convert(5,TimeUnit.SECONDS));
System.out.println(request.getHeader("user-agent"));
}
}
When I do two request in same time ,result of this execution is :
Mozilla/5.0 (Windows NT 6.1; WOW64; rv:26.0) Gecko/20100101
Firefox/26.0
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/32.0.1700.102 Safari/537.36
Mozilla/5.0 (Windows NT 6.1; WOW64; rv:26.0) Gecko/20100101
Firefox/26.0
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/32.0.1700.102 Safari/537.36
In runtime field have type com.sun.proxy.$Proxy45.
How spring make it thread-safe for read?
Just to expand a little on the comment by M. Deinum above, there are a few approaches you could use to avoid holding a reference to the request as part of the controller's state.
1) As already mentioned, directly inject the request as part of the method signature.
2) Only work with the request params that you are directly interested in by using the #RequestParam annotations in your method signatures.
3) Take a look at the Spring api doc for the RequestContextHolder class. You can do things like:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes()).getRequest();
which may or may not be useful to you.
Hope this helps :-)

Categories