Created
October 15, 2015 13:03
-
-
Save adrobisch/0876ed217d8f28588d2b to your computer and use it in GitHub Desktop.
Spring Boot security example configuration for AngularJS applications including XSRF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var angular = require('angular'); | |
var LoginController = require("./LoginController"); | |
var loginModule = angular.module('login', []); | |
loginModule.controller("LoginController", LoginController); | |
loginModule.service("login", require("./LoginApi")); | |
module.exports = loginModule; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var AutenticationInterceptor = ['$q', '$window', function($q, window) { | |
return { | |
response: function(response) { | |
return response || $q.when(response); | |
}, | |
responseError: function(rejection) { | |
if (rejection.status === 401) { | |
window.location.href = "login.html"; | |
} | |
return $q.reject(rejection); | |
} | |
}; | |
}]; | |
module.exports = AutenticationInterceptor; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<link rel="stylesheet" href="css/style.css"> | |
</head> | |
<body data-ng-app="login"> | |
<div class="container" data-ng-controller="LoginController"> | |
<div class="row"> | |
<h3>Login</h3> | |
<div class="alert alert-danger" data-ng-show="error"> | |
There was a problem logging in. Please try again. | |
</div> | |
<form role="form" data-ng-submit="login()"> | |
<div class="form-group"> | |
<label for="username">Username:</label> | |
<input type="text" | |
class="form-control" | |
id="username" | |
name="username" | |
data-ng-model="username"/> | |
</div> | |
<div class="form-group"> | |
<label for="password">Password:</label> | |
<input type="password" | |
class="form-control" | |
id="password" | |
name="password" | |
data-ng-model="password"/> | |
</div> | |
<button type="submit" class="btn btn-primary">Submit</button> | |
</form> | |
</div> | |
</div> | |
<script src="app.js"></script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function LoginApi(http) { | |
this.login = function (username, password) { | |
return http({ | |
method: 'POST', | |
url: "login", | |
data: "username=" + username + "&password=" + password, | |
headers: {'Content-Type': 'application/x-www-form-urlencoded'} | |
}); | |
}; | |
} | |
LoginApi.$inject = ["$http"]; | |
module.exports = LoginApi; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var LoginController = function(scope, window, login) { | |
scope.login = function () { | |
login.login(scope.username, scope.password) | |
.success(scope.onLoginSuccess) | |
.error(scope.onLoginError); | |
}; | |
scope.onLoginSuccess = function () { | |
scope.error = false; | |
window.location.href = "./"; | |
}; | |
scope.onLoginError = function (error) { | |
scope.error = true; | |
window.alert("error"); | |
}; | |
}; | |
LoginController.$inject = ["$scope", "$window", "login"]; | |
module.exports = LoginController; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.boot.autoconfigure.security.SecurityProperties; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.core.annotation.Order; | |
import org.springframework.core.env.Environment; | |
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | |
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | |
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | |
import org.springframework.security.core.AuthenticationException; | |
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; | |
import org.springframework.security.web.csrf.CsrfFilter; | |
import org.springframework.security.web.csrf.CsrfToken; | |
import org.springframework.security.web.csrf.CsrfTokenRepository; | |
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
import org.springframework.web.bind.annotation.RestController; | |
import org.springframework.web.filter.OncePerRequestFilter; | |
import org.springframework.web.util.WebUtils; | |
import javax.servlet.FilterChain; | |
import javax.servlet.ServletException; | |
import javax.servlet.http.Cookie; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletResponse; | |
import java.io.IOException; | |
import java.security.Principal; | |
@Configuration | |
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) | |
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { | |
public static final String LOGIN = "/login"; | |
@Autowired | |
Environment environment; | |
@Override | |
protected void configure(HttpSecurity http) throws Exception { | |
http.exceptionHandling().authenticationEntryPoint(new AjaxAwareEntryPoint(LOGIN)) | |
.and() | |
.authorizeRequests() | |
.antMatchers("/api/**").authenticated() | |
.anyRequest().permitAll() | |
.and() | |
.formLogin().loginPage(LOGIN) | |
.and() | |
.csrf().csrfTokenRepository(csrfTokenRepository()) | |
.and() | |
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class); | |
} | |
private CsrfTokenRepository csrfTokenRepository() { | |
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository(); | |
repository.setHeaderName("X-XSRF-TOKEN"); | |
return repository; | |
} | |
@Override | |
public void configure(AuthenticationManagerBuilder auth) throws Exception { | |
auth.inMemoryAuthentication() | |
.withUser("admin").password("admin").roles("ADMIN", "USER") | |
.and() | |
.withUser("user").password("user") .roles("USER"); | |
} | |
@RestController | |
public static class UserController { | |
@RequestMapping("/user") | |
public Principal user(Principal user) { | |
return user; | |
} | |
} | |
class AjaxAwareEntryPoint extends LoginUrlAuthenticationEntryPoint { | |
private static final String XML_HTTP_REQUEST = "XMLHttpRequest"; | |
private static final String X_REQUESTED_WITH = "X-Requested-With"; | |
public AjaxAwareEntryPoint(String loginFormUrl) { | |
super(loginFormUrl); | |
} | |
@Override | |
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) | |
throws IOException, ServletException { | |
if (XML_HTTP_REQUEST.equals(request.getHeader(X_REQUESTED_WITH))) { | |
response.sendError(HttpServletResponse.SC_UNAUTHORIZED); | |
} else { | |
super.commence(request, response, exception); | |
} | |
} | |
} | |
class CsrfHeaderFilter extends 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 = WebUtils.getCookie(request, "XSRF-TOKEN"); | |
String token = csrf.getToken(); | |
if (cookie==null || token!=null && !token.equals(cookie.getValue())) { | |
cookie = new Cookie("XSRF-TOKEN", token); | |
cookie.setPath("/"); | |
response.addCookie(cookie); | |
} | |
} | |
filterChain.doFilter(request, response); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment