Skip to content

Commit 32c9851

Browse files
author
Dave Syer
committed
Use Spring Security 4.1 features to cut out CSRF custom code
1 parent 7db2e7b commit 32c9851

File tree

6 files changed

+62
-267
lines changed

6 files changed

+62
-267
lines changed

auth-server/src/main/java/com/example/SocialApplication.java

Lines changed: 14 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,13 @@
1515
*/
1616
package com.example;
1717

18-
import java.io.IOException;
1918
import java.security.Principal;
2019
import java.util.ArrayList;
2120
import java.util.LinkedHashMap;
2221
import java.util.List;
2322
import java.util.Map;
2423

2524
import javax.servlet.Filter;
26-
import javax.servlet.FilterChain;
27-
import javax.servlet.ServletException;
28-
import javax.servlet.http.Cookie;
29-
import javax.servlet.http.HttpServletRequest;
30-
import javax.servlet.http.HttpServletResponse;
3125

3226
import org.springframework.beans.factory.annotation.Autowired;
3327
import org.springframework.boot.SpringApplication;
@@ -53,15 +47,10 @@
5347
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
5448
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
5549
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
56-
import org.springframework.security.web.csrf.CsrfFilter;
57-
import org.springframework.security.web.csrf.CsrfToken;
58-
import org.springframework.security.web.csrf.CsrfTokenRepository;
59-
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
50+
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
6051
import org.springframework.web.bind.annotation.RequestMapping;
6152
import org.springframework.web.bind.annotation.RestController;
6253
import org.springframework.web.filter.CompositeFilter;
63-
import org.springframework.web.filter.OncePerRequestFilter;
64-
import org.springframework.web.util.WebUtils;
6554

6655
@SpringBootApplication
6756
@RestController
@@ -82,29 +71,23 @@ public Map<String, String> user(Principal principal) {
8271

8372
@Override
8473
protected void configure(HttpSecurity http) throws Exception {
85-
// @formatter:off
86-
http.antMatcher("/**")
87-
.authorizeRequests()
88-
.antMatchers("/", "/login**", "/webjars/**").permitAll()
89-
.anyRequest().authenticated()
90-
.and().exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))
91-
.and().logout().logoutSuccessUrl("/").permitAll()
92-
.and().csrf().csrfTokenRepository(csrfTokenRepository())
93-
.and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
94-
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
74+
// @formatter:off
75+
http.antMatcher("/**").authorizeRequests().antMatchers("/", "/login**", "/webjars/**").permitAll().anyRequest()
76+
.authenticated().and().exceptionHandling()
77+
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")).and().logout()
78+
.logoutSuccessUrl("/").permitAll().and().csrf()
79+
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
80+
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
9581
// @formatter:on
9682
}
9783

9884
@Configuration
9985
@EnableResourceServer
100-
protected static class ResourceServerConfiguration
101-
extends ResourceServerConfigurerAdapter {
86+
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
10287
@Override
10388
public void configure(HttpSecurity http) throws Exception {
10489
// @formatter:off
105-
http
106-
.antMatcher("/me")
107-
.authorizeRequests().anyRequest().authenticated();
90+
http.antMatcher("/me").authorizeRequests().anyRequest().authenticated();
10891
// @formatter:on
10992
}
11093
}
@@ -114,8 +97,7 @@ public static void main(String[] args) {
11497
}
11598

11699
@Bean
117-
public FilterRegistrationBean oauth2ClientFilterRegistration(
118-
OAuth2ClientContextFilter filter) {
100+
public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
119101
FilterRegistrationBean registration = new FilterRegistrationBean();
120102
registration.setFilter(filter);
121103
registration.setOrder(-100);
@@ -146,43 +128,13 @@ private Filter ssoFilter() {
146128
private Filter ssoFilter(ClientResources client, String path) {
147129
OAuth2ClientAuthenticationProcessingFilter facebookFilter = new OAuth2ClientAuthenticationProcessingFilter(
148130
path);
149-
OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(client.getClient(),
150-
oauth2ClientContext);
131+
OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
151132
facebookFilter.setRestTemplate(facebookTemplate);
152-
facebookFilter.setTokenServices(new UserInfoTokenServices(
153-
client.getResource().getUserInfoUri(), client.getClient().getClientId()));
133+
facebookFilter.setTokenServices(
134+
new UserInfoTokenServices(client.getResource().getUserInfoUri(), client.getClient().getClientId()));
154135
return facebookFilter;
155136
}
156137

157-
private Filter csrfHeaderFilter() {
158-
return new OncePerRequestFilter() {
159-
@Override
160-
protected void doFilterInternal(HttpServletRequest request,
161-
HttpServletResponse response, FilterChain filterChain)
162-
throws ServletException, IOException {
163-
CsrfToken csrf = (CsrfToken) request
164-
.getAttribute(CsrfToken.class.getName());
165-
if (csrf != null) {
166-
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
167-
String token = csrf.getToken();
168-
if (cookie == null
169-
|| token != null && !token.equals(cookie.getValue())) {
170-
cookie = new Cookie("XSRF-TOKEN", token);
171-
cookie.setPath("/");
172-
response.addCookie(cookie);
173-
}
174-
}
175-
filterChain.doFilter(request, response);
176-
}
177-
};
178-
}
179-
180-
private CsrfTokenRepository csrfTokenRepository() {
181-
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
182-
repository.setHeaderName("X-XSRF-TOKEN");
183-
return repository;
184-
}
185-
186138
}
187139

188140
class ClientResources {

custom-error/README.adoc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,18 @@ authenticate successfully and you are not in the Spring Engineering
153153
team. If there is no match, we throw `BadCredentialsException` and
154154
this is picked up by Spring Security and turned in to a 401 response.
155155

156+
The `OAuth2RestOperations` has to be created as a bean as well (as of
157+
Spring Boot 1.4), but that's trivial because its ingredients are all
158+
autowirable by virtue of having used `@EnableOAuth2Sso`:
159+
160+
[source,java,indent=0]
161+
----
162+
@Bean
163+
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) {
164+
return new OAuth2RestTemplate(resource, context);
165+
}
166+
----
167+
156168
TIP: Obviously the code above can be generalized to other
157169
authentication rules, some applicable to Github and some to other
158170
OAuth2 providers. All you need is the `OAuth2RestOperations` and some

github/src/main/java/com/example/SocialApplication.java

Lines changed: 16 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,13 @@
1515
*/
1616
package com.example;
1717

18-
import java.io.IOException;
1918
import java.security.Principal;
2019
import java.util.ArrayList;
2120
import java.util.LinkedHashMap;
2221
import java.util.List;
2322
import java.util.Map;
2423

2524
import javax.servlet.Filter;
26-
import javax.servlet.FilterChain;
27-
import javax.servlet.ServletException;
28-
import javax.servlet.http.Cookie;
29-
import javax.servlet.http.HttpServletRequest;
30-
import javax.servlet.http.HttpServletResponse;
3125

3226
import org.springframework.beans.factory.annotation.Autowired;
3327
import org.springframework.boot.SpringApplication;
@@ -53,15 +47,10 @@
5347
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
5448
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
5549
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
56-
import org.springframework.security.web.csrf.CsrfFilter;
57-
import org.springframework.security.web.csrf.CsrfToken;
58-
import org.springframework.security.web.csrf.CsrfTokenRepository;
59-
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
50+
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
6051
import org.springframework.web.bind.annotation.RequestMapping;
6152
import org.springframework.web.bind.annotation.RestController;
6253
import org.springframework.web.filter.CompositeFilter;
63-
import org.springframework.web.filter.OncePerRequestFilter;
64-
import org.springframework.web.util.WebUtils;
6554

6655
@SpringBootApplication
6756
@RestController
@@ -82,29 +71,23 @@ public Map<String, String> user(Principal principal) {
8271

8372
@Override
8473
protected void configure(HttpSecurity http) throws Exception {
85-
// @formatter:off
86-
http.antMatcher("/**")
87-
.authorizeRequests()
88-
.antMatchers("/", "/login**", "/webjars/**").permitAll()
89-
.anyRequest().authenticated()
90-
.and().exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))
91-
.and().logout().logoutSuccessUrl("/").permitAll()
92-
.and().csrf().csrfTokenRepository(csrfTokenRepository())
93-
.and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
94-
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
74+
// @formatter:off
75+
http.antMatcher("/**").authorizeRequests().antMatchers("/", "/login**", "/webjars/**").permitAll().anyRequest()
76+
.authenticated().and().exceptionHandling()
77+
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")).and().logout()
78+
.logoutSuccessUrl("/").permitAll().and().csrf()
79+
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
80+
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
9581
// @formatter:on
9682
}
9783

9884
@Configuration
9985
@EnableResourceServer
100-
protected static class ResourceServerConfiguration
101-
extends ResourceServerConfigurerAdapter {
86+
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
10287
@Override
10388
public void configure(HttpSecurity http) throws Exception {
10489
// @formatter:off
105-
http
106-
.antMatcher("/me")
107-
.authorizeRequests().anyRequest().authenticated();
90+
http.antMatcher("/me").authorizeRequests().anyRequest().authenticated();
10891
// @formatter:on
10992
}
11093
}
@@ -114,8 +97,7 @@ public static void main(String[] args) {
11497
}
11598

11699
@Bean
117-
public FilterRegistrationBean oauth2ClientFilterRegistration(
118-
OAuth2ClientContextFilter filter) {
100+
public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
119101
FilterRegistrationBean registration = new FilterRegistrationBean();
120102
registration.setFilter(filter);
121103
registration.setOrder(-100);
@@ -144,47 +126,17 @@ private Filter ssoFilter() {
144126
}
145127

146128
private Filter ssoFilter(ClientResources client, String path) {
147-
OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationFilter =
148-
new OAuth2ClientAuthenticationProcessingFilter(path);
149-
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(client.getClient(),
150-
oauth2ClientContext);
129+
OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationFilter = new OAuth2ClientAuthenticationProcessingFilter(
130+
path);
131+
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
151132
oAuth2ClientAuthenticationFilter.setRestTemplate(oAuth2RestTemplate);
152-
UserInfoTokenServices tokenServices = new UserInfoTokenServices(
153-
client.getResource().getUserInfoUri(), client.getClient().getClientId());
133+
UserInfoTokenServices tokenServices = new UserInfoTokenServices(client.getResource().getUserInfoUri(),
134+
client.getClient().getClientId());
154135
tokenServices.setRestTemplate(oAuth2RestTemplate);
155136
oAuth2ClientAuthenticationFilter.setTokenServices(tokenServices);
156137
return oAuth2ClientAuthenticationFilter;
157138
}
158139

159-
private Filter csrfHeaderFilter() {
160-
return new OncePerRequestFilter() {
161-
@Override
162-
protected void doFilterInternal(HttpServletRequest request,
163-
HttpServletResponse response, FilterChain filterChain)
164-
throws ServletException, IOException {
165-
CsrfToken csrf = (CsrfToken) request
166-
.getAttribute(CsrfToken.class.getName());
167-
if (csrf != null) {
168-
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
169-
String token = csrf.getToken();
170-
if (cookie == null
171-
|| token != null && !token.equals(cookie.getValue())) {
172-
cookie = new Cookie("XSRF-TOKEN", token);
173-
cookie.setPath("/");
174-
response.addCookie(cookie);
175-
}
176-
}
177-
filterChain.doFilter(request, response);
178-
}
179-
};
180-
}
181-
182-
private CsrfTokenRepository csrfTokenRepository() {
183-
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
184-
repository.setHeaderName("X-XSRF-TOKEN");
185-
return repository;
186-
}
187-
188140
}
189141

190142
class ClientResources {

logout/README.adoc

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -92,46 +92,7 @@ header name. In the `WebSecurityConfigurer`:
9292
protected void configure(HttpSecurity http) throws Exception {
9393
http.antMatcher("/**")
9494
... // existing code here
95-
.and().csrf().csrfTokenRepository(csrfTokenRepository())
96-
.and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
97-
}
98-
----
99-
100-
where the `csrfHeaderFilter()` is a custom filter:
101-
102-
.SocialApplication.java
103-
[source,java]
104-
----
105-
private Filter csrfHeaderFilter() {
106-
return new OncePerRequestFilter() {
107-
@Override
108-
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
109-
FilterChain filterChain) throws ServletException, IOException {
110-
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
111-
if (csrf != null) {
112-
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
113-
String token = csrf.getToken();
114-
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
115-
cookie = new Cookie("XSRF-TOKEN", token);
116-
cookie.setPath("/");
117-
response.addCookie(cookie);
118-
}
119-
}
120-
filterChain.doFilter(request, response);
121-
}
122-
};
123-
}
124-
----
125-
126-
and the `csrfTokenRepository()` is defined here:
127-
128-
.SocialApplication.java
129-
[source,java]
130-
----
131-
private CsrfTokenRepository csrfTokenRepository() {
132-
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
133-
repository.setHeaderName("X-XSRF-TOKEN");
134-
return repository;
95+
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
13596
}
13697
----
13798

0 commit comments

Comments
 (0)