Spring Security with AngularJS Login 401 Unauthorized - angularjs

I am trying to successfully authenticate a user with Spring Security using AngularJS on the front end.
Basically what is supposed to happen is:
1) A new user should be able to fill out the registration form with a unique username and password
2) Upon submit, Angular POSTs the user/pass combination to the URL specified by spring security.
3) Spring Security verifies the account and logs the user in.
4) The user's session begins and is shown as logged in on the front end.
The hangup is occurring in step 2. The information is not successfully posted to the spring login URL. My failure handler is tripped (in the code below) and the login process is halted.
POST http://localhost:8080/libroomreserve/login 401 (Unauthorized)
Here is my Spring Security Config:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private AuthenticationFailure authFailure;
#Autowired
private AuthenticationSuccess authSuccess;
#Autowired
private EntryPointUnauthorizedHandler unauthorizedHandler;
#Autowired
private UserDetailServiceImpl userDetails;
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.formLogin()
.successHandler(authSuccess) //sets status to 200 OK
.failureHandler(authFailure) //sets status to 401 Unauthorized
.and()
.authorizeRequests()
.antMatchers("/**")
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetails);
}
Failure Handler:
#Component
public class AuthenticationFailure extends SimpleUrlAuthenticationFailureHandler{
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
Success Handler:
#Component
public class AuthenticationSuccess extends SimpleUrlAuthenticationSuccessHandler{
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_OK);
}
}
AuthenticationEntryPoint:
#Component
public class EntryPointUnauthorizedHandler implements AuthenticationEntryPoint{
#Override
public void commence(HttpServletRequest hsr, HttpServletResponse hsr1, AuthenticationException ae) throws IOException, ServletException {
hsr1.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied.");
}
}
On the AngularJS side, this is my service:
.factory('sessionService', function($http, $base64){
var session = {};
session.login = function(data){
return $http.post("/libroomreserve/login", "username=" + data.userName + "&password" + data.password,
{
headers: {
'Content-Type' : 'application/x-www-form-urlencoded'
}
})
//.then() is a "PROMISE" which is executed after initial return function is performed
.then(function(){
console.log("Logged in the user!");
localStorage.setItem("session", {});
}, function(){
console.log("Error logging in the user...");
});
};
session.logout = function(){
localStorage.removeItem("session");
console.log("User has been logged out.");
};
session.isLoggedIn = function(){
return localStorage.getItem("session") !== null;
};
return session;
})
For reference, I am following the tutorial here by Chris Henkel. I can't find any discrepancies between his code and mine.
As an aside, the newly-registered user is being logged into the database so the credentials are available for authentication.

Related

HttpServletRequest is null when using filter for JWT, headers work on postman but not localhost

I am using React for front-end and Java spring boot for backend. My api was working before I used Bcrypt to encode passwords but now there seems to be a problem with the internal filter before every api call where the response is null...
this is my WebSecurityConfig.java
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService myUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncode());
}
#Bean
public PasswordEncoder passwordEncode(){
return new BCryptPasswordEncoder();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/authenticate").permitAll()
.antMatchers("/personInfo").permitAll()
.antMatchers("/signup").permitAll().
anyRequest().authenticated().and().
addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class).exceptionHandling();
}
}
and this is my JWTRequestFilter.java
I was thinking it may have to do with the passwordEncoder() because my calls were working when i didn't use BcryptPasswordEncoder()...
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private MyUserDetailsService userDetailsService;
#Autowired
private JwtUtil jwtUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
if(request == null){
System.out.println("request is null");
}
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
i am using react for the front-end and am calling with axios
async totals(){
console.log('Bearer ', localStorage.getItem('id_token'));
let data = await axios.get("http://localhost:8080/totals", {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('id_token')
}
})
.then(this._checkStatus);
return data.request.response;
}
the api works when i use the token in postman and so the problem is between the initial request and the filter...
the error I get is -
Access to XMLHttpRequest at 'http://localhost:8080/totals' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
thanks for your time :)
If anybody ever gets the same problem, I solved it by adding this line at the end of my configure(HttpSecurity httpSecurity) method in WebSecurityConfig.java
httpSecurity.cors();
:)

Spring Oauth2 Angularjs Login Not Woking

Currently I'm developing Spring OAuth2 security project with Angularjs. I'm taking a token with oauth server and I'm parsing to headers but when I try to redirect to home page I'm thrown by "Full authentication is required to access this resource" but I loged in and client server gives an anonymousUser and access denied.
#Configuration
#EnableWebSecurity
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/login.html")
.antMatchers("/js/**")
.antMatchers("/css/**")
.antMatchers("/metronic/css/**")
.antMatchers("/metronic/js/**")
.antMatchers("/metronic/image/**")
.antMatchers("/image/**")
.antMatchers("/language/**")
.antMatchers("/404.html")
.antMatchers("/logout")
.antMatchers("/kilitEkrani.html")
.antMatchers("/metronic/css/fonts/**")
.antMatchers("/metronic/fonts/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/css/**", "/metronic/css/**").permitAll()
.and().authorizeRequests().antMatchers("/metronic/image/**", "/image/**", "/metronic/css/fonts/**", "/metronic/fonts/**").permitAll()
.and().authorizeRequests().antMatchers("/js/**", "/metronic/js/**").permitAll()
.and().httpBasic().and().authorizeRequests()
.antMatchers("/login.html", "/language/**", "/api/kullanici/user", "/logout", "/kilitEkrani.html", "/404.html").permitAll()
.anyRequest().authenticated().and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class).csrf().csrfTokenRepository(csrfTokenRepository()).and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login.html")
.permitAll().and().csrf().disable();
}
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 = 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);
}
};
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
This is my security config. Am I missing something? Help please...
I think the problem is that you make use of basic authentication in the auth server. You can try to disable the basic authentication and use form authentication instead.

Spring Oauth2 + User Registration

I have problem with Spring Oauth2 again. I know this topic is not easy to suggest sth or check the codes because we have too much configuration.
My project has 3 different servers, Authentication server, resource server and front-end server. I want to put register.html to user's registration in front-end project(under Angularjs files) but when I make request to the related url (http://localhost:7080/app/#register) its redirecting to the login page (http://localhost:9080/auth-service/login) only for a second i can see my register.html content but after that its going to login page.
The question is, where should i put this register.html, it should be under front-end project or authentication server ?
My authentication server and front-end server codes are;
#Configuration
public class AuthServerSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.parentAuthenticationManager(authenticationManager);
auth.authenticationProvider(userAuthProviderService());
}
private CsrfMatcher csrfRequestMatcher = new CsrfMatcher();
#Override
protected void configure(final HttpSecurity http) throws Exception {
/*http.csrf().disable();*/
http.csrf().requireCsrfProtectionMatcher(csrfRequestMatcher);
http
.formLogin().loginPage("/login").defaultSuccessUrl("/")
/*.failureUrl("")*/.successHandler(new AuthSuccessHandler()).permitAll()
.and()
.requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access","/register")
.and()
.authorizeRequests().anyRequest().authenticated();
}
#Bean
public UserAuthProviderService userAuthProviderService(){
return new UserAuthProviderService();
}
private class CsrfMatcher implements RequestMatcher {
#Override
public boolean matches(HttpServletRequest request) {
return false;
}
}
}
#Configuration
#EnableAutoConfiguration
#RestController
#EnableZuulProxy
#EnableOAuth2Sso
#EnableOAuth2Client
public class UIServiceMain {
public static void main(String[] args) {
SpringApplication.run(UIServiceMain.class, args);
}
#Configuration
protected static class SecurityConfiguration extends OAuth2SsoConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.logout().and()
.antMatcher("/**").authorizeRequests()
.antMatchers("/index.html", "/home.html", "/", "/login","/register.html").permitAll().anyRequest()
.authenticated().and().csrf().disable();
http.headers().frameOptions().disable(); //FOR EMBED MAP
}
//unused
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 = 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);
}
};
}
//unused
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
}
in your UI server try to create websecurity with /register.hml enabled, something like this
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/register.html")
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
}
edit:
or maybe in your current configuration remove .antMatcher("/**").authorizeRequests() and add and() .authorizeRequests().anyRequest().authenticated()
so finally it could be something like this:
#Override
public void configure(HttpSecurity http) throws Exception {
http.logout().and()
.antMatchers("/index.html", "/home.html", "/", "/login","/register.html").permitAll().anyRequest()
.authenticated()
.and().csrf().disable();
http.headers().frameOptions().disable() //FOR EMBED MAP
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
Couple of things:
I can't think of a good reason not to put your *.html anywhere other than front end server.
Also, in general, you should permit access to your static UI components publically, like #bilak mentioned:
.antMatchers("/index.html", "/home.html", "/", "/login","/register.html").permitAll()
If you are able to see register.html page at all (assuming unauthenticated user) then it is public already
Perhaps, there is a webservice call on register.html's load event that is behind Spring security that is triggering the auth flow.

Spring Security: Still can't disable redirect with logoutSuccessHandler

I am using Spring Security with Oauth2 to secure RESTful API. My WebSecurityConfig class looks like this:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationSuccessHandler authenticationSuccessHandler;
#Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(
SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/logout").permitAll()
.antMatchers("/ristore/**").authenticated()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(authenticationSuccessHandler)
.failureHandler(new SimpleUrlAuthenticationFailureHandler());
http.logout().permitAll();
http.logout().logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)));
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public RestAuthenticationSuccessHandler mySuccessHandler(){
return new RestAuthenticationSuccessHandler();
}
#Bean
public SimpleUrlAuthenticationFailureHandler myFailureHandler(){
return new SimpleUrlAuthenticationFailureHandler();
}
#Configuration
protected static class AuthenticationConfiguration extends
GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource("ldap://ldap.mdanderson.edu:389/dc=mdanderson,dc=edu");
contextSource.setUserDn("cn=ris_flow,ou=service accounts,ou=institution,ou=service accounts,dc=mdanderson,dc=edu");
contextSource.setPassword("!BMpl#tform2O15");
contextSource.setReferral("follow");
contextSource.afterPropertiesSet();
LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth.ldapAuthentication();
ldapAuthenticationProviderConfigurer
.userDnPatterns("cn={0},ou=institution,ou=people")
.userSearchBase("")
.contextSource(contextSource);
}
}
}
In order to disable redirect for logout, I added the logoutSuceessHandler.
At the front end, I wrote the following function to handle logout event with AngularJS:
$scope.logout = function() {
$http.post(SERVER + '/logout', {}).success(function() {
$rootScope.authenticated = false;
$window.localStorage.removeItem("access_token");
$location.path("/");
}).error(function(data) {
console.log("Logout failed")
$rootScope.authenticated = false;
});
};
However, I still got the following error at logout:
XMLHttpRequest cannot load http://localhost:8080/logout. The request was redirected to 'http://localhost:8080/login?logout', which is disallowed for cross-origin requests that require preflight.
I tried every solution in the following similar posts and nothing seems to stop the redirect. spring security /logout not working cross origin requests and Spring security - Disable logout redirect.
What am I missing?
EDIT
After I turned on debug mode in logging spring security, here is the output for logout request. Why is it trying match /logout request to /oauth/token?

Spring Security with REST architecture

I have been looking into a problem with Spring Security for a REST API. And before jumping into implementation I would like to get an expert advice or some sample project on github, if available.
My application will be based on REST API. And will be accessed by two clients:
Mobile Phone
Web
If I create a REST API with custom login page, then it will always be redirected to Web (as per my understanding). What when I will start consuming it with Mobile Phone?
.formLogin()
.defaultSuccessUrl("/ui/index.html#/app/dashboard")
.loginProcessingUrl("/api/upuser/verifyUser")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(new AjaxAuthenticationSuccessHandler(new SavedRequestAwareAuthenticationSuccessHandler()))
.loginPage("/ui/index.html#/access/signin")
I think from the above code it seems quite obvious that this application will be accessed from two different locations:
localhost:8080/api/ for API
localhost:8383/ui/ for WEB (Angular JS)
But, I will move both to localhost/api/ & localhost/ui/ by using nginx. So, above two will be accessed by
localhost/api/
localhost/ui/
So, my second question is what will be the best way to implement spring security:
Token Based Authentication
Session Based Authentication
Problem is as it's a stateless service so how we are going to implement session based Authentication?
Try something like this :
You should try this, may be it will help you:
#Configuration
#EnableWebSecurity
#Profile("container")
public class SOAPSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
private AuthenticationProvider authenticationProviderDB;
#Override
#Order(1)
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
#Order(2)
protected void ConfigureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProviderDB);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/scripts/**","/styles/**","/images/**","/error/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/rest/**").authenticated()
.antMatchers("/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(new AuthenticationSuccessHandler() {
#Override
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication a) throws IOException, ServletException {
//To change body of generated methods,
response.setStatus(HttpServletResponse.SC_OK);
}
})
.failureHandler(new AuthenticationFailureHandler() {
#Override
public void onAuthenticationFailure(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException ae) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
})
.loginProcessingUrl("/access/login")
.and()
.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);
}
})
.invalidateHttpSession(true)
.and()
.exceptionHandling()
.authenticationEntryPoint(new Http403ForbiddenEntryPoint())
.and()
.csrf()//Disabled CSRF protection
.disable();
}
}

Resources