├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── allanditzel │ └── springframework │ └── security │ └── web │ └── csrf │ └── CsrfTokenResponseHeaderBindingFilter.java └── test └── java └── com └── allanditzel └── springframework └── security └── web └── csrf └── CsrfTokenResponseHeaderBindingFilterTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | 27 | # OS generated files # 28 | ###################### 29 | .DS_Store 30 | .DS_Store? 31 | ._* 32 | .Spotlight-V100 33 | .Trashes 34 | ehthumbs.db 35 | Thumbs.db 36 | 37 | # IDE Files # 38 | ############# 39 | *.iml 40 | .idea/ 41 | 42 | # Maven Files # 43 | ############### 44 | target/ 45 | target-grunt/ 46 | src/main/webapp/static/node_modules/ 47 | 48 | # Git Files # 49 | ############# 50 | *.orig 51 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 Allan Ditzel 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/aditzel/spring-security-csrf-filter.svg?branch=master)](https://travis-ci.org/aditzel/spring-security-csrf-filter) 2 | #Spring Security CSRF Token Filter 3 | 4 | The idea behind this filter is to be able to use Spring Security to build a Single Page Application with whatever 5 | front end technology you would like such as Ember, Angular, Backbone, etc. 6 | 7 | By default, Spring Security assumes that you are going to be rendering all your pages on the server, so you are 8 | expected to use their expression language to print out the CSRF tokens to make it available to your UI layer. This 9 | filter is meant to allow you to automatically expose the CSRF token data from Spring on all HTTP response headers. 10 | 11 | #Installation: 12 | 13 | This can be installed via Maven: 14 | 15 | ```` 16 | 17 | com.allanditzel 18 | spring-security-csrf-token-filter 19 | 1.1 20 | 21 | ```` 22 | 23 | #Usage: 24 | 25 | If you are using JavaConfig you just have to add it to a configure block for HttpSecurity: 26 | 27 | ```Java 28 | protected void configure(HttpSecurity http) throws Exception { 29 | CsrfTokenResponseHeaderBindingFilter csrfTokenFilter = new CsrfTokenResponseHeaderBindingFilter(); 30 | http.addFilterAfter(csrfTokenFilter, CsrfFilter.class); 31 | } 32 | ``` 33 | 34 | #Credits: 35 | Credit goes to the authors of the great discussion on stackoverflow.com: 36 | 37 | http://stackoverflow.com/questions/20862299/with-spring-security-3-2-0-release-how-can-i-get-the-csrf-token-in-a-page-that 38 | 39 | #License: 40 | Apache 2.0 41 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 4.0.0 19 | 20 | com.allanditzel 21 | spring-security-csrf-token-filter 22 | 1.2-SNAPSHOT 23 | jar 24 | 25 | Spring Security CSRF Token Filter 26 | https://github.com/aditzel/spring-security-csrf-filter 27 | A Spring Security Filter that binds the existing CSRF token values to response headers. 28 | 29 | 30 | 31 | aditzel 32 | Allan Ditzel 33 | allan@allanditzel.com 34 | https://github.com/aditzel 35 | 36 | 37 | 38 | 39 | 40 | The Apache Software License, Version 2.0 41 | http://www.apache.org/licenses/LICENSE-2.0.txt 42 | repo 43 | 44 | 45 | 46 | 47 | https://github.com/aditzel/spring-security-csrf-filter/issues 48 | GitHub Issues 49 | 50 | 51 | 52 | https://github.com/aditzel/spring-security-csrf-filter 53 | scm:git:git://github.com/aditzel/spring-security-csrf-filter.git 54 | scm:git:git@github.com:aditzel/spring-security-csrf-filter.git 55 | HEAD 56 | 57 | 58 | 59 | 60 | ossrh 61 | https://oss.sonatype.org/content/repositories/snapshots/ 62 | 63 | 64 | 65 | 66 | UTF-8 67 | 4.11 68 | 1.9.5 69 | 3.2.0.RELEASE 70 | 3.1.0 71 | 1.7.7 72 | 2.1.1 73 | 2.9.1 74 | 1.5 75 | 1.6 76 | 2.5 77 | 78 | 79 | 80 | 81 | 82 | javax.servlet 83 | javax.servlet-api 84 | ${servlet.version} 85 | provided 86 | 87 | 88 | junit 89 | junit 90 | ${junit.version} 91 | test 92 | 93 | 94 | org.mockito 95 | mockito-all 96 | ${mockito.version} 97 | test 98 | 99 | 100 | org.springframework.security 101 | spring-security-web 102 | ${spring.security.version} 103 | provided 104 | 105 | 106 | org.springframework.security 107 | spring-security-config 108 | ${spring.security.version} 109 | provided 110 | 111 | 112 | org.slf4j 113 | slf4j-api 114 | ${slf4j.version} 115 | test 116 | 117 | 118 | org.slf4j 119 | slf4j-simple 120 | ${slf4j.version} 121 | test 122 | 123 | 124 | org.slf4j 125 | jcl-over-slf4j 126 | ${slf4j.version} 127 | test 128 | 129 | 130 | 131 | 132 | 133 | 134 | javax.servlet 135 | javax.servlet-api 136 | 137 | 138 | junit 139 | junit 140 | 141 | 142 | org.mockito 143 | mockito-all 144 | 145 | 146 | org.springframework.security 147 | spring-security-web 148 | 149 | 150 | org.springframework.security 151 | spring-security-config 152 | 153 | 154 | org.slf4j 155 | slf4j-api 156 | 157 | 158 | org.slf4j 159 | slf4j-simple 160 | 161 | 162 | org.slf4j 163 | jcl-over-slf4j 164 | 165 | 166 | 167 | 168 | 169 | 170 | org.apache.maven.plugins 171 | maven-source-plugin 172 | ${maven-source-plugin.version} 173 | 174 | 175 | attach-sources 176 | 177 | jar-no-fork 178 | 179 | 180 | 181 | 182 | 183 | org.apache.maven.plugins 184 | maven-javadoc-plugin 185 | ${maven-javadoc-plugin.version} 186 | 187 | 188 | attach-javadocs 189 | 190 | jar 191 | 192 | 193 | 194 | 195 | 196 | org.sonatype.plugins 197 | nexus-staging-maven-plugin 198 | ${nexus-staging-maven-plugin.version} 199 | true 200 | 201 | ossrh 202 | https://oss.sonatype.org/ 203 | 204 | 205 | 206 | org.apache.maven.plugins 207 | maven-release-plugin 208 | ${maven-release-plugin.version} 209 | 210 | true 211 | false 212 | release-sign-artifacts 213 | deploy 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | release-sign-artifacts 222 | 223 | 224 | performRelease 225 | true 226 | 227 | 228 | 229 | 230 | 231 | org.apache.maven.plugins 232 | maven-gpg-plugin 233 | ${maven-gpg-plugin.version} 234 | 235 | 236 | sign-artifacts 237 | verify 238 | 239 | sign 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | -------------------------------------------------------------------------------- /src/main/java/com/allanditzel/springframework/security/web/csrf/CsrfTokenResponseHeaderBindingFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Allan Ditzel 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.allanditzel.springframework.security.web.csrf; 18 | 19 | import org.springframework.security.web.csrf.CsrfToken; 20 | import org.springframework.web.filter.OncePerRequestFilter; 21 | 22 | import javax.servlet.ServletException; 23 | import javax.servlet.http.HttpServletRequest; 24 | import javax.servlet.http.HttpServletResponse; 25 | import java.io.IOException; 26 | 27 | /** 28 | * Binds a {@link org.springframework.security.web.csrf.CsrfToken} to the {@link HttpServletResponse} 29 | * headers if the Spring {@link org.springframework.security.web.csrf.CsrfFilter} has placed one in the {@link HttpServletRequest}. 30 | * 31 | * Based on the work found in: Stack Overflow 32 | * 33 | * @author Allan Ditzel 34 | * @since 1.0 35 | */ 36 | public class CsrfTokenResponseHeaderBindingFilter extends OncePerRequestFilter { 37 | protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf"; 38 | protected static final String RESPONSE_HEADER_NAME = "X-CSRF-HEADER"; 39 | protected static final String RESPONSE_PARAM_NAME = "X-CSRF-PARAM"; 40 | protected static final String RESPONSE_TOKEN_NAME = "X-CSRF-TOKEN"; 41 | 42 | @Override 43 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain filterChain) throws ServletException, IOException { 44 | CsrfToken token = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME); 45 | 46 | if (token != null) { 47 | response.setHeader(RESPONSE_HEADER_NAME, token.getHeaderName()); 48 | response.setHeader(RESPONSE_PARAM_NAME, token.getParameterName()); 49 | response.setHeader(RESPONSE_TOKEN_NAME , token.getToken()); 50 | } 51 | 52 | filterChain.doFilter(request, response); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/com/allanditzel/springframework/security/web/csrf/CsrfTokenResponseHeaderBindingFilterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Allan Ditzel 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.allanditzel.springframework.security.web.csrf; 18 | 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.mockito.Mock; 23 | import org.mockito.runners.MockitoJUnitRunner; 24 | import org.springframework.security.web.csrf.CsrfToken; 25 | 26 | import javax.servlet.FilterChain; 27 | import javax.servlet.ServletException; 28 | import javax.servlet.http.HttpServletRequest; 29 | import javax.servlet.http.HttpServletResponse; 30 | import javax.servlet.http.HttpSession; 31 | 32 | import java.io.IOException; 33 | 34 | import static org.mockito.Mockito.verify; 35 | import static org.mockito.Mockito.verifyNoMoreInteractions; 36 | import static org.mockito.Mockito.when; 37 | 38 | /** 39 | * Test for {@link CsrfTokenResponseHeaderBindingFilter}. 40 | * 41 | * @author Allan Ditzel 42 | * @since 1.0 43 | */ 44 | @RunWith(MockitoJUnitRunner.class) 45 | public class CsrfTokenResponseHeaderBindingFilterTest { 46 | private CsrfTokenResponseHeaderBindingFilter filter; 47 | 48 | @Mock 49 | HttpServletRequest request; 50 | 51 | @Mock 52 | HttpServletResponse response; 53 | 54 | @Mock 55 | FilterChain filterChain; 56 | 57 | @Mock 58 | HttpSession session; 59 | 60 | @Mock 61 | CsrfToken token; 62 | 63 | @Before 64 | public void setUp() { 65 | filter = new CsrfTokenResponseHeaderBindingFilter(); 66 | } 67 | 68 | @Test 69 | public void shouldContinueProcessingFilterChainIfTokenNotPresentInRequest() throws ServletException, IOException { 70 | when(request.getAttribute(CsrfTokenResponseHeaderBindingFilter.REQUEST_ATTRIBUTE_NAME)).thenReturn(null); 71 | 72 | filter.doFilterInternal(request, response, filterChain); 73 | 74 | verify(request).getAttribute(CsrfTokenResponseHeaderBindingFilter.REQUEST_ATTRIBUTE_NAME); 75 | verify(filterChain).doFilter(request, response); 76 | verifyNoMoreInteractions(request, response, filterChain); 77 | } 78 | 79 | @Test 80 | public void shouldBindCsrfValuesToResponseHeadersWhenTokenIsPresentInRequest() throws ServletException, IOException { 81 | String headerName = "headerName"; 82 | String paramName = "paramName"; 83 | String tokenValue = "token"; 84 | 85 | when(request.getAttribute(CsrfTokenResponseHeaderBindingFilter.REQUEST_ATTRIBUTE_NAME)).thenReturn(token); 86 | when(token.getHeaderName()).thenReturn(headerName); 87 | when(token.getParameterName()).thenReturn(paramName); 88 | when(token.getToken()).thenReturn(tokenValue); 89 | 90 | filter.doFilterInternal(request, response, filterChain); 91 | 92 | verify(request).getAttribute(CsrfTokenResponseHeaderBindingFilter.REQUEST_ATTRIBUTE_NAME); 93 | verify(token).getHeaderName(); 94 | verify(token).getParameterName(); 95 | verify(token).getToken(); 96 | verify(response).setHeader(CsrfTokenResponseHeaderBindingFilter.RESPONSE_HEADER_NAME, headerName); 97 | verify(response).setHeader(CsrfTokenResponseHeaderBindingFilter.RESPONSE_PARAM_NAME, paramName); 98 | verify(response).setHeader(CsrfTokenResponseHeaderBindingFilter.RESPONSE_TOKEN_NAME, tokenValue); 99 | verify(filterChain).doFilter(request, response); 100 | verifyNoMoreInteractions(token, request, response, filterChain); 101 | } 102 | } --------------------------------------------------------------------------------