$http request giving 403 after using Spring Security? - java

Below is my SecurityConfiguration class, I am using.
#SpringBootApplication
#RestController
public class WebMvcConfig {
#Configuration
protected static class SecurityConfiguration extends
WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/**").authenticated()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
After startup, as soon as I hit my URL (http://localhost:8080/TestApp/), it takes me to the default login page and when I enter the default user Id (user) and password (printed on the console), it take me to my index.html page mapped by the "/" URL via my AngularJS routing. I am able to navigate through the UI but as soon as I submit any $http request (I am trying with a POST request), it gives me 403 on the browser console with my $http request URL.
Can someone help?

Error 403 means that you are forbidden from accessing all the resources.
If you inspect the error details, more probably you will have a message such as Expected CSRF token not found.
From v4 onwards, spring security enables by default csrf protection. This is a good practice as csrf attacks force an end user to execute unwanted actions on a web application in which they’re currently authenticated.
So in a dev environment, adding http.csrf().disable(); will solve your problem. But you should consider adding a csrf token when you want to move to a prod env.

Related

Spring Security returns login page despite permitAll

I know there is a lot of questions like this, but I could not find an answer which solves my case.
Here is my config:
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.POST, "/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.logout().permitAll();
}
}
And here is my endpoint I want users to have access without logging in:
#Slf4j
#RestController
public class MyController {
#PostMapping(value = "/", consumes = MediaType.TEXT_PLAIN_VALUE)
public void acceptAnonymously(HttpEntity<String> requestEntity) {
log.debug("body: {}", requestEntity.getBody());
}
}
So basically, I want to allow making unauthenticated POST requests to localhost:8080. Everything else should be authenticated. But when I hit localhost:8080 with postman, this is what I get:
So, CSRF stands for Cross-Site Request Forgery and I believe is enabled by default with Spring Web/Security. When it is enabled, you need to properly pass the correct csrf token to your app in order to access your application otherwise you will get thrown a 403 forbidden type error. Alternatively, there are other means of authenticating users if you so desired.
.csrf().disable()
Actually disabling CSRF is not a good idea for all situations. CSRF is enabled by default and as a result, the CSRF token is added to the HttpServletRequest attribute named _csrf. So you only need to add it to your requests.
If you are using Thymeleaf for your html templates you could add something like this to your forms:
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>

Spring security User Principal keeps coming back as anonymousUser

I am working on this override below and even though I login with proper credentials I keep getting 403 or 500 errors. My filter ordering is correct as it is the same as our other apps so I guess I am wrong in the anyRequest() part that I am missing something. How do I get my credentials to persist?
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().hasAnyRole("read_only", "read_write", "read_write_delete")
.and()
.addFilter(securityContextPersistenceFilter())
.addFilterAfter(preAuthFilter(), SecurityContextPersistenceFilter.class)
.addFilterAfter(ssoFilter(), PreAuthFilter.class)
.addFilterAfter(new RequestContextFilter(), SSOFilter.class)
.addFilterAfter(new CrossFramePreventionFilter(), RequestContextFilter.class)
.addFilterAfter(characterEncodingFilter, CrossFramePreventionFilter.class)
.addFilterAfter(exceptionTranslationFilter(), CharacterEncodingFilter.class)
.addFilterAfter(new NoCacheFilter(), ExceptionTranslationFilter.class);
}
The reason for the failure is because order matters in the filter chain and I had the preAuthFilter listed before the ssoFilter. The ssoFilter sets and removes UserPrincipal.authenticatedUser which the preAuth filter uses to create an authentication token. Since I had preAuthFilter first the authenticated user was null and therefore token creation failed. Working its way through the filter chain if there still is no token created then spring automatically adds an AnonymousAuthenticationFilter which creates an AnonymousAuthenticationToken with "anonymousUser" and "ROLE_ANONYMOUS" like i saw in logs.
Reversing the order of those filters allowed for a valid PreAuthenticatedAuthenticationToken to be created and then everything worked as expected.

Spring Security seems to be using a session even though I am using SessionCreationPolicy.STATELESS

I am using Spring Security to secure an application using OAuth. I wanted to make it stateless so I have a custom authentication filter that authenticates based on a token from the client. My configuration for Spring Security is as follows:
#Override
protected void configure(HttpSecurity http) throws Exception {
// require https
http.requiresChannel().antMatchers("/**").requiresSecure().and().portMapper().http(80).mapsTo(443);
http.antMatcher("/**").authorizeRequests().anyRequest().authenticated().and()
.cors().and()
.csrf().disable()
.addFilterBefore(customOAuth2Filter(), BasicAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
As you can see, I am using SessionCreationPolicy.STATELESS which should mean that Spring Security never uses a session. However, if I login and then try to access a protected endpoint without a token(say, if I close the tab and then open a protected endpoint in a new tab, or if I just refresh the page), the application will give me the data at the endpoint instead of asking me to login again.
So it seems like a session is being used even though I am using SessionCreationPolicy.STATELESS. However, I did notice that the JSESSIONID of each request being sent does not match the JSESSIONID in the set-cookie header of the previous response. Not sure why that is or where the new JSESSIONID is coming from.
How do I make it so subsequent requests without the token will ask for login again? Any insight into what is happening here would be appreciated, thanks.

Redirecting user to oauth2 authorization server to get token Spring Boot

I have 2 apps running, one is resource server where I have the info that needs authentication to view the text. Then I have authorization server that gives tokens. Right now I can use postman or Insomnia, add the auth_url, token_url, client_id, client_secret and I get the token. I add the token to header and i get do a get request to my resource server using header, and it works just fine.
Now i have no idea how to implement redirection from my resource server directly. Like when I go to
localhost:9000/home
I'd like to get redirected to:
localhost:9001/login
where I login with my inmemory user then it redirects me back to localhost:9000/home and I see the message.
What would be the best way to implement a way for user to access information on localhost:9000/home. You go to localhost:9000/home, it goes to authorization server on localhost:9001, you log in with username and password. Approve the grant, and it puts you back to localhost:9000/home and then you can see the text, what was previously protected, because you didn't have token to access it.
ResourceServer.java
#SpringBootApplication
#RestController
#EnableResourceServer
#EnableOAuth2Client
public class SampleResourceApplication extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**").hasRole("user")
.anyRequest().authenticated();
}
#Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
public static void main(String[] args) {
SpringApplication.run(SampleResourceApplication.class, args);
}
#RequestMapping("/home")
public String home() {
return "this is home";
}
}
and my properties looks like:
server:
port: 900
security:
oauth2:
client:
client-id: foo
client-secret: foosecret
access-token-uri: http://localhost:9001/auth/oauth/token
user-authorization-uri: http://localhost:9001/auth/oauth/authorize
grant-type: USER
auto-approve-scopes: true
resource:
user-info-uri: http://localhost:9001/auth/user
Let's separate the agents: You have the user (i.e. you, also know as the resource owner), the authorization server, the resource server and the client (the application that access your urls, i.e. your browser).
Normally, this happens in your situation:
When your client access the resource server, it receives a 401. Depending of your implementation, you could also directly redirect the client to your AS (using a simple redirect response).
Your AS prompts you for credentials. After validating them, it issues a token for you. You can then use this token to access the RS.
What you're trying to get (if I understand correctly) is to redirect with the token automatically. To achieve this, you can simply pass the url you tried to reach (i.e. localhost:9000/home) when you redirect to your AS at the end of step 1. Your AS hten prompts the user for credentials, generate the token, stores it as a cookie (in the case of a browser), and redirects you to the url he received (localhost:9000/home).
EDIT: what's the resulting code for the redirection.
When you get to the configure, you first check if the user is authenticated. If he is, then all's fine, but if he isn't, you must catch this event and start your redirection. This can be done using the exceptionHandling method of the chaining http:
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**").hasRole("user")
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint());
}
private AuthenticationEntryPoint authenticationEntryPoint() {
return new AuthenticationEntryPoint() {
// You can use a lambda here
#Override
public void commence(HttpServletRequest aRequest, HttpServletResponse aResponse,
AuthenticationException aAuthException) throws IOException, ServletException {
aResponse.sendRedirect(MY_AS_URL + "?redirect_uri=localhost:9001/home");
}
};
}
Unfortunately, I am not familiar with the Spring framework, but hopefully this helps anyways:
OAuth is an authorization protocol. It does not handle authentication (see also: "What is the difference between authentication and authorization?" on ServerFault).
If I understand you correctly, you want users to be redirected to /login when they go to /home and aren't already logged-in. This step has nothing to do with OAuth, it must be part of your application's security / firewall setup.
Please also note that there is a difference between logging in (authenticating) on the authorization server and actually granting the application the right to access your resources (authorization). First you have to prove who you are and only then can you give access to your stuff. These are two separate steps.

Spring Security - doesn't access database

I am using spring boot and very new to spring security, but I wanted basic security to my web application. What I did was add on my pom.xml the needed dependencies and added this java class to my project:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**","/event/**","/ticket/**")
.hasRole("USER")
.and()
.formLogin();
}
}
After running my web application, I run into the login page, where I put user/password and then it goes to my web application. However, the commands don't work. I am pushing some buttons that should send signals to my MySql database, but nothing happens. It's like the front-end isn't connected to the back-end anymore. I am using AngularJS for front-end and a View Controller that navigates between pages. Rest of the application is REST-based. Any idea why this might happen?
Later Edit: Now I understand, the problem that I have is that after authenticating, I get 403 status codes on my end-points. Any idea how I might fix it?
Later Editv2: Looks like I don't get authorized on my POST requests, my GET ones work fine...here are some of my POST end-points: /event/buy_ticket/{id} , /ticket//cancel_ticket/{id}
angular.min.js:101 POST http://localhost:8080/event/buy_ticket/2 403 ()
I even tried to explicitly say it to permit it, but I still get 403...
http.authorizeRequests()
.antMatchers("/**","/event/**","/ticket/**","/event/buy_ticket/2")
.permitAll()
.and()
.formLogin();
Later later later edit:
Disabling csrf worked
Getting 403 Forbidden error codes means that Spring is receiving your requests but choosing to stop processing them. From the Wiki page on HTTP 403:
Authentication was provided, but the authenticated user is not
permitted to perform the requested operation.
If I had to wager, I would say the problem is that you have not specified what resources and endpoints should be accessible and how. If memory serves me right, Spring Security will, by default, lock down everything super tightly so you need to explicitly tell it what to leave open. Below is a sample from my own security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() // require authorization
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // for the CORS preflight check
.antMatchers("/login", "/api/open/**", "/resources/**").permitAll() // the open API endpoints and resources
.antMatchers("/logout", "/api/secured/**").authenticated(); // lock down these endpoints
...additional configurations...
}
All endpoints that should be freely available are prefaced with "/api/open/" while all endpoints that should be protected by Spring Security are prefaced with "/api/secured/". The exceptions are the logout and login endpoints, since those tie into Spring Security directly.
Here's a great blog post - and the related repo - that shows off how to implement Spring Security that plays nice with AngularJS, even as a Single Page Application, which are notoriously annoying to secure.
Edit: You might be running up against CSRF protection, which is enabled by default in Spring Security - see this post. CSRF will allow HTTP "safe" methods like GET, HEAD, and OPTION but block "unsafe" methods like PUT, POST, DELETE, etc if those methods do not provide the proper token (since no CSRF was configured, those request don't have a token -> blocked). For testing purposes, you can disable it by adding http.csrf().disable() to the configure() method.

Categories