My trial code to remove cookies
.logout()
.logoutUrl("/access/logout")
.logoutSuccessHandler(new LogoutSuccessHandler() {
#Override
public void onLogoutSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication a) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
})
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
This code is of logout security with HttpSecurity.
Here I am getting issue to delete cookies
in logoutSuccessHandler in logger it shows cookies = 1 and i.e JESSESIONID
So my problem is how to delete cookie at the time of logout
or if is there any another way or any way in angular then please tell me
After going through many blogs on spring security I got answer,
.invalidateHttpSession(true) :- it is used to clear our session
and
.deleteCookies("JSESSIONID") :- it is used to delete cookies
use this after your success handler so that it will remove all cookies after logout
EDIT
And if any one want to handle cookies from frontend side then you can handle it using ng-cookies in AngularJs.
Related
As I am new the springboot, I got stuck with a senario, pls help me out.
I have custom login page where it takes a username and password and validate it. If user
present, in my database then jwt token is generated, I have implemented this and this case is
working. Now my problem is
I am trying to integrate the google sign-in. But while integrating the google sign-in, I am
getting authorized as anonymous user and I couldn't able proceed further. This is not the
case I dont want.
when ever user logged with google sign in option user must be able to sign in and could able to
generate the jwt token. How can I solve my problem. I am using technology springboot and reactjs.
My security configuration code.
public class Springsec extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception{
http
.csrf()
.disable()
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/index.html","/error/**")
.permitAll()
.anyRequest().authenticated().and().formLogin();
/* http
.exceptionHandling()
.authenticationEntryPoint(auth)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class);
}
}
Is Below one is the only way to solve my problem or is there any way to solve the problem
google sign in must me authorized by reactjs then we have to send access token to the server
and generate the jwt token.
If there is any other way pls help out. If possible send me sample code.
If My guess is the only way to solve the problem, Then simply say yes.
Thank you.
please help me out.
I have written about this here. Your configure method could look something like:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests(t -> t.anyRequest().authenticated())
.formLogin(t -> t.loginPage("/login").permitAll())
.logout(t -> t.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").permitAll());
try {
ClientRegistrationRepository repository =
getApplicationContext().getBean(ClientRegistrationRepository.class);
if (repository != null) {
http.oauth2Login(t -> t.clientRegistrationRepository(repository)
.userInfoEndpoint(u -> u.oidcUserService(oidcUserService))
.loginPage("/login").permitAll());
}
} catch (Exception exception) {
}
http.sessionManagement(t -> t.maximumSessions(-1).sessionRegistry(sessionRegistry()));
}
and the OAuth2 configuration (in application.yaml):
---
spring:
security:
oauth2:
client:
registration:
google:
client-id: XXXXXXXXXXXXXXXXXXXX
client-secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
provider:
google:
user-name-attribute: email
I am implementing a Login page with AngularJS to authenticate against LDAP server. The authentication at the back end is done by Spring Security. Basically username and password are sent to server via a post request which is handled by Spring without an explicit handler.
When I submit the login form, the post request returns 302 and redirect to another url based on whether credentials are valid or not. If the password is correct, it will initiate GET request to "http://localhost:8080/". If password is wrong, it redirects to "http://localhost:8080/login?error". This is known behavior of Spring Security. According to Dave Syer's article, "the Spring Security default behaviour is to send a 302 on success and failure, and Angular will follow the redirect, so we would have to actually parse the response from that". In Dave's tutorial, he used a helper function to verify the authentication in the general case. I don't think it would work for LDAP authentication though.
I found another very similar post Spring Boot and Security with custom AngularJS Login page. Although it does not have an official answer, based on the last comment, it seems modifying paths in .antMatchers in Java config may have resolved the issue. However I played with my security config (see below) back and forth and it did not seem to help.
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().and()
.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class)
.csrf().disable()
.authorizeRequests()
.antMatchers("/login/", "/login","login/")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin();
}
Although 302 does not produce any response, client somehow knows which url to redirect to based on credential's validity. My understanding is that the server must have told client if authentication succeeds or not in a "secret" way. If I capture and parse this hidden information before client sends GET request, I could make the authentication work with Angular. Something like this (partially pseudo code):
app.controller("LoginCtrl", ['$scope', '$location',
function($scope, $location){
$scope.authenticate = function() {
loginFactory.login($scope.username, $scope.password) {
// pseudo code starts
if (redirect.path == 'localhost') {
$location.path('/main');
}
else {
$location.path('/loginfail');
console.log("Login failed");
}
}
}]);
The question is how to diagnose the 302 and identify the nature of the response before redirect happens? Worst case scenario, I could let the redirect to start, and grab response.path from the GET request and decide whether login is successful. But I try not to go down that path. Need suggestion badly.
After hours of google search, I found a way to suppress the 302 and thus avoid redirects based on this article. The resolution is to inject a custom authentication success handler in the form login filter and SimpleUrlAuthenticationFailureHandler to handle failed authentication.
I'm looking to implement protections against CSRF attacks in my API, which I developed using GAE Endpoints with oAuth2 required for all methods.
Before implementing any specific protection I'm trying to actually break my app (CSRF looked simple at first glance). But just can't make it work.
When I reference my endpoint in another page, the browser adds the cookie information but not the Authorization header with the bearer access token. This does not seem to be enough, because my endpoints automatically return 401 with a www-authenticate:Bearer realm="https://accounts.google.com/" header.
As I said, I have no specific protection against CSRF. But does using Google Cloud Endpoints with oAuth2 under HTTPS grants me protection against this type of attack "for free"?
--edit to address comment
I tried a simple CSRF attack. I got a page up with an <img src="https://bla-bla-bla-appspot.com/_ah/api/myapi/v1/resource.getMethod">. Then I accessed this page while I had my app opened in another tab, so my browser would send my authentication information. And it does send the cookie, but not my oAuth token).
I didn't even tried doing a POST, if I "hack" a GET it would be great already.
OAUth 2.0 explicitly protects against CSRF via the use of a non-guessable state parameter which is generated by the client and validated by the server. Even if an attacker was able to trick a client into visiting a URL to authorize a malicious token, the state parameter would not match that of the client and the request would be denied.
The Google Cloud Endpoints libraries handle this bit of the OAuth spec for you, so you're in the clear.
Oauth2 requires all requests to have the bearer access token either as an HTTP header (use XMLhttpRequest from javascript to set the header and make the request) or as a URL query parameter (access_token). An attacker won't know this secret value, so would not be able to create a URL which would pass validation.
Here is (I hope) helpful java snippet from my Kuoll remote debugger for web apps.
package com.kuoll.server.filters;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CrossOriginFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse) response;
resp.addHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
resp.setHeader("Access-Control-Allow-Headers", "origin, content-type, accept");
chain.doFilter(request, response);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
Replace * in resp.addHeader("Access-Control-Allow-Origin", "*"); to your Origin (if needed).
web.xml
<filter-mapping>
<filter-name>CrossOriginFilter</filter-name>
<url-pattern>/api/*</url-pattern>
</filter-mapping>
Recently I start implementing a token based security system with angularjs and spring mvc. The idea is the following:
1. Visit /user/authenticate to get a security token and save the token to local storage
2. For each request sent by the angularJS client, use an interceptor to inject a X-Auth-Token header to the request.
In my spring back-end I have implemented an AuthenticationTokenProcessingFilter and a CustomAuthenticationEntryPoint. The first for extracting the token from the header and check if it is valid and the second to return a 401 unauthorized status when a request is not authenticated.
Please find some details about my back end code
AuthenticationController.java
#RestController
#RequestMapping(value="user")
public class AuthenticationController {
#RequestMapping(value="authenticate", method = RequestMethod.POST)
public ResponseEntity<?> login(#RequestParam("email") String email,
#RequestParam("password") String password) {
//Check if user is valid and return token
}
}
SecurityConfig.java
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UsersRepository usersRepo;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {...}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(
new AuthenticationTokenProcessingFilter(usersRepo),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(this.corsFilter(), UsernamePasswordAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable().exceptionHandling()
.and()
.httpBasic()
.authenticationEntryPoint(new CustomAuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/user/authenticate").permitAll()
.antMatchers("/**").authenticated()
.anyRequest().authenticated();
}
#Bean
public CORSFilter corsFilter() {
return new CORSFilter();
}
}
CORSFilter.java
public class CORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, Origin, X-Auth-Token");
response.addHeader("Access-Control-Expose-Headers", "X-Auth-Token");
chain.doFilter(req, res);
}
}
Now I am using the following angularjs code in order to query the /user/authenticate endpoint which is not behind firewall
return $http.post(baseUrl + 'user/authenticate', 'email='+username+'&password='+password,
{
headers : {
'content-type' : 'application/x-www-form-urlencoded'
}
}
);
When I use the above code everything works. However If I remove the headers parameter from the request my angularjs client sends an OPTION request (rather than a POST request - I imagine this is related to my CORS filter) and my back end sends a 401 Unauthorised response.
Could you please give me a few more details why this is happening?
Thank you in advance!
I think I had a similar (the same?) problem and I tweaked my filter chain in WebSecurityConfigurerAdapter::configure(HttpSecurity http) a little bit to solve it.
I think what happens in your case is that the browser sends an OPTIONS request to figure out whether CORS is allowed or not by your server. The OPTIONS request is handled in your filter chain and will eventually match
.antMatchers("/**").authenticated()
Now you'll probably end up in your CustomAuthenticationEntryPoint() and return a 401.
You could add:
.antMatchers(HttpMethod.OPTIONS, "/user/authenticate/").permitAll()
However, I think you still would get problems afterwards. When the client now wants to access other resources the token has to be sent in the header. Most likely this will result in another OPTIONS request which does not contain your token and so you will eventually end up with the same problem.
So what I did was to explicitly allow all OPTIONS request without checking for authentication.
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
Now you bypass your security filter and you won't return a 401. I think that this does not affect the security of the application as long as you won't handle OPTIONS requests by yourself in a controller and allow someone to access critical data.
OPTIONS request is a so called preflight request:
To protect resources against cross-origin requests that could not
originate from certain user agents before this specification existed a
preflight request is made to ensure that the resource is aware of this
specification.
Basically, it means that an OPTIONS request is issued whenever the resource (backend server) didn't supply Origin and Access-Control-* in the response (to the client). You'll need CORS activated as soon as your backend and client (webapp) are located under different domains e.g. backend is available under domainA and your client is available under domainB. Even domainA:80 and domain:8080 are treated as different domains.
The problem is to get the CSRF tokens working between Spring Security and Angular.
Spring Security CSRF Token Interceptor for Angular seems like something that should do the job, but there is no 'X-CSRF-TOKEN' in the HEAD response from the server.
My current tiny implementation is available in GitHub (Tag v.1.0) and I would appreciate a lot if somebody who knows the topic would have a quick look on the code, the problem should be easy to spot.
Based on the documentation, I am under the impression that CSRF should have been enabled automatically, but that seems not to be the case.
I am using Spring Boot and prefer the annotation-based configuration over XML, if something needs to be configured differently.
Any other approaches to make Spring Security work against Angular?
Angular looks for a cookie called "XSRF-TOKEN" I believe, so the easiest thing to do for the client is to send that. You can do it in a Filter for instance (example from https://github.com/spring-guides/tut-spring-security-and-angular-js/blob/master/single/src/main/java/demo/UiApplication.java#L65):
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
.getName());
if (csrf != null) {
Cookie cookie = new Cookie("XSRF-TOKEN", csrf.getToken());
cookie.setPath("/");
response.addCookie(cookie);
}
filterChain.doFilter(request, response);
}
};
}
Update: since spring security 4.2 the correct cookie name for angular is used by default if you use the cookie csrf repository(the link is still the best source), i.e. there is no longer any need for a custom filter. Example:
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
I am answering the question myself as there was a hidden one in the original GitHub repository: Issue #1.
The solution was to add a couple of lines of Java code that adds the CSRF parameters as Http message headers.
I added a working solution to the GitHub repo with Tag v.2.0.