Skip to content

Instantly share code, notes, and snippets.

@silverprize
Last active July 25, 2017 06:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save silverprize/05ca08ebb2ed6037f8428aa13c221629 to your computer and use it in GitHub Desktop.
Save silverprize/05ca08ebb2ed6037f8428aa13c221629 to your computer and use it in GitHub Desktop.
spring security authentication configuration for content-type application/json
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