I am building an application where authentication is done by spring security for HTTP handlers, for HTTP I've disabled csrf protection, and now I want to disable csrf for spring web socket, but I can't figure out how to accomplish this, I've already tried many different approaches but no one seems to be working. If it is impossible to disable csrf for WebSocket how to get a csrf token? (I tried setting up the csrf endpoint to obtain a token but it is not work, and all tutorials I've found are outdated)
Thanks in advance!
web socket security config:
#Configuration
#EnableWebSocketSecurity
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Bean
AuthorizationManager<Message<?>> messageAuthorizationManager(
MessageMatcherDelegatingAuthorizationManager.Builder messages) {
messages.anyMessage().permitAll();
return messages.build();
}
#Override
protected boolean sameOriginDisabled() {
return true;
}
}
security config:
#Configuration
#EnableWebSecurity(debug = true)
public class SecurityConfig {
#Autowired
private JwtFilter jwtFilter;
#Bean
SecurityFilterChain securityFilterChain(HttpSecurity HTTP) throws Exception {
return http.addFilterBefore(jwtFilter, BasicAuthenticationFilter.class)
.cors(AbstractHttpConfigurer::disable)
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/authenticate").permitAll()
.requestMatchers("/createchatroom").authenticated()
.requestMatchers("/public/*").permitAll()
.requestMatchers("/private/*").permitAll()
.requestMatchers("/ws/**").authenticated()
.requestMatchers("/register").permitAll()
.requestMatchers("/csrf").authenticated()
.requestMatchers("/addEmployeeToFavorites").hasAnyAuthority(EMPLOYEE.name(),
ADMIN.name())
.requestMatchers("/addChatRoomToFavorites")
.hasAnyAuthority(EMPLOYEE.name(), ADMIN.name())
.requestMatchers("/home").hasAnyAuthority(EMPLOYEE.name(), ADMIN.name()))
.build();
}
}
By default, Spring Security requires the CSRF token in any CONNECT message type. This ensures that only a site that has access to the CSRF token can connect. Since only the same origin can access the CSRF token, external domains are not allowed to make a connection.
Spring Security 4.0 has introduced authorization support for WebSockets through the Spring Messaging abstraction.
In Spring Security 5.8, this support has been refreshed to use the AuthorizationManager API.
To configure authorization using Java Configuration, simply include the #EnableWebSocketSecurity annotation and publish an AuthorizationManager<Message<?>> bean or in XML use the use-authorization-manager attribute. One way to do this is by using the AuthorizationManagerMessageMatcherRegistry to specify endpoint patterns like so:
#Configuration
#EnableWebSocketSecurity
public class WebSocketSecurityConfig {
#Bean
AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
messages
.simpDestMatchers("/user/**").authenticated()
return messages.build();
}
}
Any inbound CONNECT message requires a valid CSRF token to enforce the Same Origin Policy.
The SecurityContextHolder is populated with the user within the simpUser header attribute for any inbound request.
Our messages require the proper authorization. Specifically, any inbound message that starts with "/user/" will require ROLE_USER. Additional details on authorization can be found in [websocket-authorization]
At this point, CSRF is not configurable when using #EnableWebSocketSecurity, though this will likely be added in a future release.
To disable CSRF, instead of using #EnableWebSocketSecurity, you can use XML support or add the Spring Security components yourself, like so:
Java
#Configuration
public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new AuthenticationPrincipalArgumentResolver());
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
AuthorizationManager<Message<?>> myAuthorizationRules = AuthenticatedAuthorizationManager.authenticated();
AuthorizationChannelInterceptor authz = new AuthorizationChannelInterceptor(myAuthorizationRules);
AuthorizationEventPublisher publisher = new SpringAuthorizationEventPublisher(this.context);
authz.setAuthorizationEventPublisher(publisher);
registration.interceptors(new SecurityContextChannelInterceptor(), authz);
}
}
web.xml
<websocket-message-broker use-authorization-manager="true" same-origin-disabled="true">
<intercept-message pattern="/**" access="authenticated"/>
</websocket-message-broker>
On the other hand, if you are using the legacy-websocket-configuration and you want to allow other domains to access your site, you can disable Spring Security’s protection. For example, in Java Configuration you can use the following:
#Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
...
#Override
protected boolean sameOriginDisabled() {
return true;
}
}
References
WebSocket Security - Spring Security
Related
I'm new to Spring boot and Spring Security. I have microservice project using Spring boot. And in my gateway app, I use OAuth2 for authentication. The authentication provider is from my organization and it is OIDC implementation.
I'm using oauth2 resource server to authenticate the bearer token, by configuring jwk-set-uri and jwk-set-uri properties.
spring-boot-starter-web => 2.6.7
spring-boot-starter-oauth2-resource-server => 2.6.7
spring-security => 5.6.3
application.properties
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://<org-auth-url>.com
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://<org-auth-url>/<jwk-uri>
With just above configuration, the authentication works fine. So I have not added any Security Config class also. But for authorization and other processing like to get user data in Controller, I need the user information and AD group details.
I have the user information endpoint URL. And when I test it in postman client, the response contains user information along with AD groups.
How to get the User details for Authorization?
Ok.
You've already added the required uri. Good.
Now you need to add some configuration:
#Configuration
#EnableWebSecurity
public class OAuth2ResourceServerSecurityConfiguration {
#Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
String jwkSetUri;
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(HttpMethod.GET,
///// more your requestMatchers /////
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
#Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri).build();
}
}
Now you should be able to receive jwt claims in your controllers with #AuthenticationPrincipal annotation.
#RestController
public class YourController {
#GetMapping("/")
public String doAnything(#AuthenticationPrincipal Jwt jwt) {
return jwt.getSubject();
}
}
Please add more info and I'll try to explain it better :-)
==== UPD ====
Really useful official manual on this.
Official code samples
I am developing RESTfull API for BFF(Bridge for front-end) application using Spring boot.
In my application it is not required to do any Authorization/Authentication and the only thing that i should do is to pass the JWT token from Authorization header to the HTTP client that calls another API. My goal was not to grant access to the endpoints of my application for the requests that don't contain Authorization header with token.
I reached my goal using the next config in WebSecurityConfigurerAdapter
#Configuration
#Order(1)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.requestMatcher(new RequestHeaderRequestMatcher("Authorization"))
.authorizeRequests()
.antMatchers("/**").permitAll();
}
}
This configuration seems like working as needed but I get the empty response body if i don't provide the token in Authorization header:
Response image
Is it possible to configure somehow Spring Security to throw an exception and handle it with #ExceptionHandler in #ControllerAdvice or somewhere else to return the custom JSON object with error?
I am really new with Spring Security so please tell me if my configuration in WebSecurityConfigurerAdapter is not good enough for my purposes.
I am trying to familiarize myself with Spring Security, in particular migrating from Spring Security OAuth to Soring Security (as in the following example/guide https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Migration-Guide).
However, I am seeming to only get 403 Forbidden errors. I am accessing from Postman and am using my company's existing OAuth server. I am able to get a token from the auth server, so I know I have those credentials correct and I have verified what roles the OAuth user has.
I am using the following dependencies:
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
implementation 'org.springframework.boot:spring-boot-starter-web'
This is the simple endpoint I am attempting to access:
#RestController
public class AppController
{
#GetMapping("/hello")
public String hello()
{
return "hello";
}
}
This is my application.yml file:
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: <<company-website-here>>/uaa/oauth/token_keys
And this is my security configuration class:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/hello").hasRole("MY_ROLE")
.anyRequest().authenticated()
.and()
.oauth2ResourceServer().jwt();
}
}
I can't seem to figure out why I seem to only get 403 errors. I have also tried adding #EnableWebSecurity to the security config class, but that didn't make a difference. Adding the auth server URL explicitly to the server and/or manually creating a JwtDecoder didn't do the trick either; it appears the url is being automatically picked up from the yml file, based on its property name.
I am trying to move away from using the org.springframework.security.oauth.boot dependency and ResourceServerConfigurerAdapter.
I had to add my own converter like so:
private static class JwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken>
{
private final Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter;
public JwtAuthenticationConverter()
{
this.jwtGrantedAuthoritiesConverter = jwt -> jwt
.getClaimAsStringList("authorities")
.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
#Override
public final AbstractAuthenticationToken convert(#NonNull Jwt jwt)
{
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
return new JwtAuthenticationToken(jwt, authorities, jwt.getClaimAsString("client_id"));
}
}
Then had to add this to the main security config:
.jwtAuthenticationConverter(new JwtAuthenticationConverter());
There may be a couple of things happening.
As you're migrating to Spring Security 5, you may need to extract your authorities manually. Check this post and it's correct answer.
You are using hasRole function and this will append "ROLE_" before your authority/role. So if the role on your JWT token is not ROLE_JWT_ROLE you should use
hasTransaction.
I'm creating a new Spring REST application with some basic services and entities.
I added Spring Security and without overriding any class, i just added to application.properties a user and password.
So far so good, i opened Postman to try out a endpoint and it always return 401 to my requests.
I tried in postman set the authorization via "Basic Auth" (is what header WWW-Authenticate asks), tried "Digest auth" using the "Realm" value from the header. But none of it works.
Here is what i have in my application.properties
spring.security.user.name=root
spring.security.user.password=root
This is my request
https://imgur.com/URM3TGD
(Sorry i can't embbed the image because of my reputation)
And here is the endpoint
#PostMapping("saveUsuario")
public Usuario saveUsuario(Usuario usuario) {
return usuarioRepository.save(usuario);
}
(If possible) i don't want to override any Spring Security class, just "use as it".
Thank you!
So here is what i found.
Thanks to #jzheaux we discover that the problem was with the csrf configuration (Using POST request).
So i was forced to override the class WebSecurityConfigurerAdapter to disable it.
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
But then, the endpoints could be called without authentication!
So, this is the final code:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.cors();
http.authorizeRequests().anyRequest().fullyAuthenticated();
http.httpBasic();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
}
}
First disable the CSRF.
Then enable Cors.
I set that i want any request to be fully authenticated
The challenge type is HTTP basic
I disable the creation of cookies so it'll always ask for credentials.
So far so good, it's working!
Per https://docs.spring.io/spring-boot/docs/1.5.0.RELEASE/reference/htmlsingle/#boot-features-security
you should change your password with
security.user.password=root
instead of spring.security.user.password=root
similar security properties that are overridable are in the #ConfigurationProperties class: SecurityProperties.java
See https://github.com/spring-projects/spring-boot/blob/v1.5.0.RELEASE/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityProperties.java
I work with a web app that exposes a REST API to mobile apps. I upgraded my Spring Boot version from 1.5.3.RELEASE to 2.0.2.RELEASE and after fixing a few breaking changes I am facing one that I cannot solve.
I followed this Spring Boot 2.0 Migration Guide and Spring Boot Security 2.0 and also looked into Security changes in Spring Boot 2.0 M4.
The issue is that the app uses JWT authentication and there is an endpoint (/auth/login) accepts user credentials and generates a long-lived JWT in return.
There is a filter that examines the JWT token sent by the client and determines whether the client can access the requested resource.
Custom security config is like this:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfiguration {
#Configuration
#Order(1)
public class AuthenticationConfiguration extends WebSecurityConfigurerAdapter {
// Some dependencies omitted
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// we don't need CSRF because JWT token is invulnerable
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// don't create session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers(HttpMethod.GET, "/version/**").permitAll()
// Some more antMatchers() lines omitted
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
// disable page caching
httpSecurity.headers().cacheControl();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationTokenFilter(jwtTokenUtil);
}
}
#Configuration
#Order(2)
public class ClientVersionSupportConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry
.addInterceptor(versionCheckingFilter())
.addPathPatterns("/**")
.excludePathPatterns("/error"); // Some more endpoints omitted
}
#Bean
public VersionCheckingInterceptor versionCheckingFilter() {
return new VersionCheckingInterceptor();
}
}
}
Note the .antMatchers("/auth/**").permitAll() line. /auth endpoints should be accessible without JWT since the JWT has not yet been generated when the user has not yet logged in.
Before upgrading Spring Boot, it worked fine, now it is not working. Login attemps are rejected by the filter that checks the JWT. Looks like .permitAll() is not making the requests pass through. /version/** does not work either. Hitting it from the browser gives an error page.
I also tried to delete lines from the config until this remained:
httpSecurity
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
It did not help. Could you please help with restoring the original behavior?
Do you have a base path for you api, e.g. /api ?
The server.contextPath default Spring property name has changed to server.servlet.context-path.
So if you use a default base path for you api, you won't find the endpoints where you expect them. Unless you update the property ;)