How to disable basic http auth for OPTIONS with SpringBoot Security - java

I have small rest service that is protected with default Spring Boot security config. It by default requires authorization on every http method including OPTIONS, chrome however does not give a flying duck and won't include authorization header in preflight request which results in 401 response.
How can I disable http basic auth on specific method? So far I tried:
#Configuration
#EnableWebMvc
#EnableGlobalMethodSecurity(securedEnabled = true)
public class Config {
}
And in controller:
#CrossOrigin(origins = {"http://localhost:4200"}, maxAge = 4800)
#RestController
public class MainController {
#Secured("IS_AUTHENTICATED_ANONYMOUSLY")
#RequestMapping(method = RequestMethod.OPTIONS)
public ResponseEntity handle() {
return new ResponseEntity(HttpStatus.OK);
}
}
Did not work obviously.

In the method
#Override
protected void configure(HttpSecurity http) throws Exception
{
add
.antMatchers(HttpMethod.OPTIONS, "/path/to/skip/check").permitAll()

Related

spring boot end point works with browser but not with postman

I am new to spring security, I have written test endpoint with google oauth2, I can authencitace with web browser and my end point works, but not working with postman,
here is my properties
spring.security.oauth2.client.registration.google.client-id={{CLIENT_ID}}
spring.security.oauth2.client.registration.google.client-secret={{SECRET}}
spring.security.oauth2.client.registration.google.scope=openid,profile,email
Here is my controller
#RestController
public class UserController {
#GetMapping()
public String get() {
return "testing java 18";
}
}
Here is my security config
#Configuration
#EnableWebSecurity(debug = true)
public class SecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.oauth2Login(withDefaults());
return http.build();
}
}
with above configuration, if I test http://localhost:8080 on browser it redirects to google signing and the text testing java 18 appears.
But when I use postman with below config
Type: `OAuth 2.0`
Add authorization to data to: `Request Header`
Configure New Token:
Token Name: {{SOME_RANDOM_NAME}}
Grant Type: `Authorization Code`
Callback URL: `https://oauth.pstmn.io/v1/callback`
Auth URL: `https://accounts.google.com/o/oauth2/auth`
Access Token URL: `https://oauth2.googleapis.com/token`
Client ID: `{{CLIENT_ID}}` same used in application.properties
Client Secret: `{{CLIENT_SECRET}}` same used in application.properties
Scope: `profile email openid`
State: empty
Client Authentication: `Send as basic auth header`
With above if I hit Get New Access Token, I do get new Access Token and id_token with google signing, after I hit Use Token I and send GET on the endpoint I get 403
Also If I use id_token and change Type to Bearer Token I get the same.
with older version If I use below Securityy config, Endpoint works when I send my id_token as Type to Bearer Token
#Configuration
#EnableWebSecurity(debug = false)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").fullyAuthenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.oauth2ResourceServer().jwt();
}
}
I tried searching in the forum but didn't; got much.

Spring security on endpoint using Cognito IAM role?

I am trying to restrict specific endpoints on a Spring boot service depending on what role they have set in the OAuth2 credentials.
This is the endpoint
#RestController
#RequestMapping("/api/admin")
public class AdminController {
#GetMapping(produces = "application/json")
public TestResponse get() {
return new TestResponse("Admin API Response");
}
}
This is then secured using SecurityConfiguration bean
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.and()
.authorizeRequests()
.antMatchers("/login", "/", "/home", "/logout", "/ping").permitAll()
.antMatchers("/api/admin").hasRole("arn:aws:iam::xxxxxx:role/spring-sso-test-ADMIN")
.antMatchers("/api/user").hasRole("arn:aws:iam::xxxxxx:role/spring-sso-test-USER")
.and()
.oauth2Login()
.and()
.logout()
.logoutSuccessUrl("/logout");
}
}
I debugged the Principal and can see the correct IAM role in the list of attributes cognito:roles list
However when I hit the endpoint I get a HTTP 403 Unauthorized. Meaning that the user has authenticated successfully, but Spring does not recognize or understand the attributes or how to map them?
I tried using the #Secured annotation but that didn't change anything.
#Secured("arn:aws:iam::xxxxxx:role/spring-sso-test-ADMIN")
#GetMapping(produces = "application/json")
public TestResponse get() {
return new TestResponse("Admin API Response");
}
How do I allow this to work using an IAM role defined in AWS Cognito?
When you use the hasRole DSL method, Spring Security adds the ROLE_ prefix to your authority. So, the authority arn:aws:iam::xxxxxx:role/spring-sso-test-ADMIN will become ROLE_arn:aws:iam::xxxxxx:role/spring-sso-test-ADMIN.
You should use the hasAuthority method instead.
Additionally, you should take the cognito:roles from the attributes and add in the authorities, since it's the property that Spring Security will query to get the authorities.
To map the authorities you can use a OAuth2UserService:
#Bean
SecurityFilterChain app(HttpSecurity http) throws Exception {
http
.oauth2Login(oauth2 -> oauth2
.userInfoEndpoint(userInfo -> userInfo
.oidcUserService(this.oidcUserService())
...
)
);
return http.build();
}
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
// your custom implementation
}
More details in the documentation.

No 'Access-Control-Allow-Origin' header is present on the requested resource in angular 9 and spring boot 2

I added spring security to the spring boot application and I have some api end points that needs to be called no matter user login or not.(I mean these are the rest end points where I need to retrieve data in my front side angular).
So,I config it as:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, proxyTargetClass = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService customUserDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().
disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**")
.permitAll()
.antMatchers("/books").permitAll()
.antMatchers("/api/v1/search/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
}
I have all the api exposed from : http://localhost:8080/api/v1/ like:
http://localhost:8080/api/v1/books
http://localhost:8080/api/v1/bookcategory
I have configured using .antMatchers("/api/v1/search/**"),and my config for restendpoint is:
#RequestMapping("/api/v1")
#RestController
#CrossOrigin(origins ="http://localhost:4200")
public class BasicAuthController {
#GetMapping(path = "/basicauth")
public AuthenticationBean basicauth() {
System.out.println("hitted here");
return new AuthenticationBean("You are authenticated");
}
}
I allowed the csfr policy using:
#Configuration
public class RepositoryConfig implements RepositoryRestConfigurer{
#Autowired
private EntityManager entityManager;
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(entityManager.getMetamodel().getEntities().stream()
.map(Type::getJavaType).toArray(Class[]::new));
//to handle cross origin
config.getCorsRegistry().addMapping("/**").allowedOrigins("http://localhost:4200");
}
}
BookRepository.java
public interface BookRepository extends JpaRepository<Book,Long> {
#RestResource(path = "categoryid")
Page<Book> findByCategoryId(#Param("id") Long id,Pageable pageable);
//to get book by searching
#RestResource(path = "searchbykeyword")
Page<Book> findByNameContaining(#Param("xyz") String keyword,Pageable pageable);
}
front side I have angular 9 as:
auth.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class AuthService {
// BASE_PATH: 'http://localhost:8080'
USER_NAME_SESSION_ATTRIBUTE_NAME = 'authenticatedUser';
public username: String;
public password: String;
constructor(private http: HttpClient) {
}
authenticationService(username: String, password: String) {
return this.http.get(`http://localhost:8080/api/v1/basicauth`,
{ headers: { authorization: this.createBasicAuthToken(username, password) } }).pipe(map((res) => {
this.username = username;
this.password = password;
this.registerSuccessfulLogin(username, password);
}));
}
createBasicAuthToken(username: String, password: String) {
return 'Basic ' + window.btoa(username + ":" + password)
}
}
//i didnot pasted all the codes.
So,I get error as when I goto link http://localhost:4200/books:
I have some projects using Angular+SpringBoot with security and I create a specific Bean to handle with CORS and I never have problem. If you can try, add this method bellow in your WebSecurityConfig class:
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200", "http://localhost:8080"));
configuration.setAllowedMethods(Arrays.asList("GET", "PUT", "POST","OPTIONS", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("authorization","content-type"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
The problem is CORS which is a security feature of your browser. It ensures that only resources form the same domain (and port!) can be accessed. Your Angular development server and the Tomcat run on a different port which causes the request to be declined. You have to configure CORS. However, you should know what you are doing because you are basically disabling a security feature. Usually it is not a problem tho. You can do this by adding the annotation #CrossOrigin to your controller methods or by using the Java configuration. For the second cause, I'm sure you'll easily find it on Google :)
CORS (Cross-Origin Resource Sharing) is a security feature of your browser that prevent authorized sites from using Resources from another origin
in nutshell it happens if your site on x:y origin and requesting resources from a:y, x:b or a:b origins (different port and/or domain)
what exactly happens in nutshell when this is the case
if you made a get (or post...) request from another origin, the browser will first make an option request to the same endpoint, if it's succeeded and has all the allowing headers it will make the get request, if not it will throw the error specifying why it was denied and don't make the original request
so we have two cases now, it's either the headers is returned only on the get request, but not the options one, or it's never returned

Postman 403 Forbidden message

I made some api with REST Spring. GET request works fine in Postman but when I try to do POST request I receive this error :
{
"timestamp": "2018-09-25T06:39:27.226+0000",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/cidashboard/projects"
}
This is my controller :
#RestController
#RequestMapping(ProjectController.PROJECT_URL)
public class ProjectController {
public static final String PROJECT_URL = "/cidashboard/projects";
private final ProjectService projectService;
public ProjectController(ProjectService projectService) {
this.projectService = projectService;
}
#GetMapping
List<Project> getAllProjects(){
return projectService.findAllProjects();
}
#GetMapping("/{id}")
Project getProjectById(#PathVariable int id) {
return projectService.findProjectById(id);
}
#PostMapping
void addProject(#RequestBody Project newProject) {
projectService.saveProject(newProject);
}
}
Security configuration
initial I wanted to work with ldap, but in my application properties i left only the conection at database....................................................................................................................................................
#EnableGlobalMethodSecurity
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**").permitAll();
// .anyRequest().fullyAuthenticated();
// .and()
// .formLogin().loginPage("/login").permitAll()
// .failureUrl("/login-error");
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups")
.contextSource(contextSource())
.passwordCompare()
//.passwordEncoder(new LdapShaPasswordEncoder())
.passwordAttribute("userPassword");
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/static/**"); // #3
}
#Bean
public DefaultSpringSecurityContextSource contextSource() {
return new DefaultSpringSecurityContextSource(Arrays.asList("ldap://localhost:8389/"), "dc=springframework,dc=org");
}
}
Enable spring security with #EnableWebSecurity usage.By default enables csrf support, you have to disable it to prevent Forbidden errors.
#Override
protected void configure(HttpSecurity http) throws Exception {
http //other configure params.
.csrf().disable();
}
PS: 415 unsupported type --> add to your mapping like this annotation for which type of data is sending from Postman.
#PostMapping(consumes = "application/json")
void addProject(#RequestBody Project newProject) {
projectService.saveProject(newProject);
}
In case you want to solve this issue without compromising security, you can send the xsrf-token with your request in postman.
Create a new environment in Postman (e.g. "local").
Create a new variable in this environment (e.g. "xsrf-token")
Go back to your request and make sure the right environment is selected on the top right corner ("local" in this case)
In your POST request, add a header with key "X-XSRF-TOKEN" and value "{{csrf-token}}"
In the "tests" tab, add following code:
var xsrfCookie = pm.cookies.get('XSRF-TOKEN')
pm.environment.set("xsrf-token", xsrfCookie)
The first time you make this request, you will still get a 403, but you'll also receive a cookie with the xsrf-token. The script will copy this token in the environment variable and the next requests you'll make use the appropriate token.
Check the "User-Agent" included in Headers section, If not add the "User-Agent" field
I I was also getting the same error. I found the solution using a different application, not postman {Insomnia REST Client}.
When I went back to postman after wondering, I realized that it is related to permissions in spring security. So after setting the permissions it will work.

Spring Security disable rememberMe doesn't work

I have Spring Boot application. It is necessary to send Authorization header on every request otherwise deny access to resource.
But if I made request with this header once I can send requests without it and receive content of resource. Why? Where am I wrong in code?
Spring Security configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.rememberMe().disable()
.csrf().disable()
.authorizeRequests().anyRequest().fullyAuthenticated().and()
.httpBasic().and()
.formLogin().disable()
.logout().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("1").password("1").roles("USER");
}
}
Controller:
#RestController
public class FooController {
#RequestMapping("/foo")
public Bar bar() {
Bar bar = new Bar();
return bar;
}
}
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
Disable create session. Session Management

Categories