Closed jamesdh closed 11 years ago
oh and by the way....THANK YOU FOR MAKING THIS PLUGIN!!!!! :+1:
Thanks! It would be cool if you could have a look at this issue and submit a PR. I am having a lot of "data migration fun" with my day job at the moment and will be only partially net connected next week while on holiday.
On 14 Aug 2013, at 4:03 PM, James Hardwick notifications@github.com wrote:
oh and by the way....THANK YOU FOR MAKING THIS PLUGIN!!!!!
— Reply to this email directly or view it on GitHub.
Hi @jamesdh,
I ran into the same problems, but I think I found a solution regarding to this article:
http://www.bmchild.com/2013/05/spring-security-return-401-unauthorized.html
Here are the steps to solve this issue:
1) Create a Java class called CustomBasicAuthenticationEntryPoint
and put it into your Java Sources directory
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.util.ELRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomAuthenticationEntryPoint extends
BasicAuthenticationEntryPoint {
private static final RequestMatcher requestMatcher = new ELRequestMatcher(
"hasHeader('X-Requested-With','XMLHttpRequest')");
public CustomAuthenticationEntryPoint() {
super();
}
public CustomAuthenticationEntryPoint(String realmName) {
setRealmName(realmName);
}
@Override
public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException) throws IOException, ServletException {
if(isPreflight(request)){
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
} else if (isRestRequest(request)) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
} else {
super.commence(request, response, authException);
}
}
/**
* Checks if this is a X-domain pre-flight request.
* @param request
* @return
*/
private boolean isPreflight(HttpServletRequest request) {
return "OPTIONS".equals(request.getMethod());
}
/**
* Checks if it is a rest request
* @param request
* @return
*/
protected boolean isRestRequest(HttpServletRequest request) {
return requestMatcher.matches(request);
}
}
2) Open resources.groovy
and add the following lines:
// Place your Spring DSL code here
beans = {
basicAuthenticationEntryPoint(CustomAuthenticationEntryPoint) { bean ->
realmName = 'Your Realm'
}
}
That should do the trick ...
Regards, Christian
Hi @christianjunk! That's a bit of a different problem then the one I described. You're speaking to the header which causes the browser to pop-up the auth prompt. The problem I've described is that the OPTIONS request in particular shouldn't be authenticated at all and allowed to proceed without getting a 401 error. But I'm guessing your approach is the same path or pretty close to the one I should take to fix it, so thanks for the help!
@jamesdh Please take a look at the code of my updated answer. This will check if the request is an OPTION request or not. If so it will even pass if the resource need authentication.
@christianjunk Yea, that's pretty close to what I did last night :) I'm not going to do anything w/ the header which causes a browser prompt for login, as I believe (?) that is standard/expected behavior anyways. We can look more into that separately.
One question...does the CORS standard dictate that the OPTIONS preflight not contain any content (hence your SC_NO_CONTENT) and only headers? I just simply checked if it was an OPTIONS request and called the overridden method if so, otherwise made no changes (as in I didn't set the status to no content).
Thanks for the PR. I merge it on the weekend.
Hi - christianjunk it looks like RequestMatcher is an deprecated method. Could you please provide the newer one? Also one can answer on very similar question? http://stackoverflow.com/questions/32725249/cors-filter-issue-for-authentrypoint
Since Spring Security 4.1, this is the proper way to make Spring Security support CORS (also needed in Spring Boot 1.4/1.5):
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// http.csrf().disable();
http.cors();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(ImmutableList.of("*"));
configuration.setAllowedMethods(ImmutableList.of("HEAD",
"GET", "POST", "PUT", "DELETE", "PATCH"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Reference: http://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/cors.html
Since Spring Security 4.1, this is the proper way to make Spring Security support CORS (also needed in Spring Boot 1.4/1.5):
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // http.csrf().disable(); http.cors(); } @Bean public CorsConfigurationSource corsConfigurationSource() { final CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(ImmutableList.of("*")); configuration.setAllowedMethods(ImmutableList.of("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH")); final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
Reference: http://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/cors.html
I know it's been a while. In grails 5, how do I make this configuration even being loaded. It seems like it is not loaded for me. Do I need to map it somehow?
When using this plugin along with the Spring Security basic auth filter, CORS breaks down because it seems the auth is getting applied to the OPTIONS requests as well. This causes the options requests to return with a status of 401 which in turn breaks CORS on pretty much any browser.
It seems fair that the basic auth filter would get applied to all request types. I'm just wondering if there is some way we can override the default filter behavior when used in conjunction with this plugin. I'd be willing to put in the work and submit a PR but am just throwing this out there for ideas before I get started on it!