Last active
July 25, 2017 06:21
-
-
Save silverprize/05ca08ebb2ed6037f8428aa13c221629 to your computer and use it in GitHub Desktop.
spring security authentication configuration for content-type application/json
This file contains 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 javax.servlet.ServletException; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletResponse; | |
import java.io.IOException; | |
import java.util.Map; | |
import java.util.UUID; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.beans.factory.annotation.Value; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.http.HttpStatus; | |
import org.springframework.security.authentication.AuthenticationServiceException; | |
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | |
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | |
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; | |
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | |
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | |
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | |
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer; | |
import org.springframework.security.core.Authentication; | |
import org.springframework.security.core.AuthenticationException; | |
import org.springframework.security.core.userdetails.UserDetailsService; | |
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | |
import org.springframework.security.web.AuthenticationEntryPoint; | |
import org.springframework.security.web.authentication.AuthenticationFailureHandler; | |
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; | |
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | |
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; | |
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; | |
import org.springframework.security.web.util.matcher.RequestMatcher; | |
import com.fasterxml.jackson.core.type.TypeReference; | |
import com.fasterxml.jackson.databind.ObjectMapper; | |
@Configuration | |
@EnableWebSecurity | |
public class SecurityConfig extends WebSecurityConfigurerAdapter { | |
private final static String NAME_LOGIN_DATA = "data.login"; | |
private final UserDetailsService userDetailsService; | |
@Value("${app.apiRootPath}") | |
private String apiRootPath; | |
@Autowired | |
public SecurityConfig(UserDetailsService userDetailsService) { | |
this.userDetailsService = userDetailsService; | |
} | |
@Autowired | |
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { | |
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder()); | |
} | |
@Override | |
protected void configure(HttpSecurity http) throws Exception { | |
String key = UUID.randomUUID().toString(); | |
http | |
.csrf().disable() | |
.exceptionHandling() | |
.authenticationEntryPoint(new AuthenticationApiEntryPoint()) | |
.and() | |
.apply(new AuthenticationFilterConfigurer<>(new AuthenticationFilter(), apiRootPath + "/login")) | |
.successHandler(new SuccessHandler()) | |
.failureHandler(new FailureHandler()) | |
.and() | |
.rememberMe() | |
.key(key) | |
.rememberMeServices(new RememberMeServices(key, userDetailsService)) | |
.and() | |
.authorizeRequests() | |
.antMatchers(apiRootPath + "/**") | |
.authenticated() | |
.anyRequest() | |
.permitAll(); | |
} | |
private static class AuthenticationFilterConfigurer<H extends HttpSecurityBuilder<H>> extends | |
AbstractAuthenticationFilterConfigurer<H, AuthenticationFilterConfigurer<H>, AuthenticationFilter> { | |
AuthenticationFilterConfigurer(AuthenticationFilter authenticationFilter, String defaultLoginProcessingUrl) { | |
super(authenticationFilter, defaultLoginProcessingUrl); | |
} | |
@Override | |
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) { | |
return new AntPathRequestMatcher(loginProcessingUrl, "POST"); | |
} | |
} | |
private static class AuthenticationFilter extends UsernamePasswordAuthenticationFilter { | |
private ObjectMapper objectMapper = new ObjectMapper(); | |
@Override | |
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { | |
if (!request.getMethod().equals("POST")) { | |
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); | |
} | |
try { | |
Map<String, Object> data = objectMapper.readValue(request.getInputStream(), new TypeReference<Map<String, Object>>() {}); | |
request.setAttribute(NAME_LOGIN_DATA, data); | |
String username = (String) data.getOrDefault(this.getUsernameParameter(), ""); | |
String password = (String) data.getOrDefault(this.getPasswordParameter(), ""); | |
username = username.trim(); | |
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); | |
// Allow subclasses to set the "details" property | |
this.setDetails(request, authRequest); | |
return this.getAuthenticationManager().authenticate(authRequest); | |
} catch (IOException e) { | |
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod(), e); | |
} | |
} | |
} | |
private static class AuthenticationApiEntryPoint implements AuthenticationEntryPoint { | |
@Override | |
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { | |
response.sendError(HttpStatus.FORBIDDEN.value(), HttpStatus.FORBIDDEN.getReasonPhrase()); | |
} | |
} | |
private static class SuccessHandler implements AuthenticationSuccessHandler { | |
@Override | |
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { | |
// customize response data | |
} | |
} | |
private static class FailureHandler implements AuthenticationFailureHandler { | |
@Override | |
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { | |
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); | |
} | |
} | |
private static class RememberMeServices extends TokenBasedRememberMeServices { | |
RememberMeServices(String key, UserDetailsService userDetailsService) { | |
super(key, userDetailsService); | |
} | |
@Override | |
protected boolean rememberMeRequested(HttpServletRequest request, String parameter) { | |
if (super.rememberMeRequested(request, parameter)) { | |
return true; | |
} | |
Map data = (Map) request.getAttribute(NAME_LOGIN_DATA); | |
String paramValue = (String) data.get(parameter); | |
if (paramValue != null && paramValue.equalsIgnoreCase("true")) { | |
return true; | |
} | |
if (logger.isDebugEnabled()) { | |
logger.debug(String.format("Did not send remember-me cookie (principal did not set parameter '%s')", parameter)); | |
} | |
return false; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment