WebMvcConfig.java
import java.util.Arrays;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
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.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;
import com.liu.security.GdprFilter;
import com.liu.security.Http401AuthenticationFailureHandler;
import com.liu.security.MssAuthenticationFilter;
import com.liu.security.MssAuthenticationProvider;
import com.liu.security.MssUserDetailsService;
import com.liu.security.SimpleTextAuthenticationSuccessHandler;
@Configuration
@EnableWebSecurity
@Import(SecurityProblemSupport.class)
public class WebMvcConfig extends WebSecurityConfigurerAdapter {
private static final String LOGIN_PAGE_URL="/login";
private static final String GDC_TASK_API_URI="/api/v3/gdc/task";
private final MssUserDetailsService mssUserDetailsService;
private final SecurityProblemSupport problemSupport;
public WebMvcConfig(
MssUserDetailsService mssUserDetailsService,
SecurityProblemSupport problemSupport) {
this.mssUserDetailsService = mssUserDetailsService;
this.problemSupport = problemSupport;
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
@Bean
SessionAuthenticationStrategy sessionAuthenticationStrategy() {
ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlAuthenticationStrategy
= new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
concurrentSessionControlAuthenticationStrategy.setExceptionIfMaximumExceeded(false);
concurrentSessionControlAuthenticationStrategy.setMaximumSessions(1);
return new CompositeSessionAuthenticationStrategy(Arrays.asList(
concurrentSessionControlAuthenticationStrategy,
new RegisterSessionAuthenticationStrategy(sessionRegistry())
));
}
@Bean
MssAuthenticationFilter mssAuthenticationFilter() throws Exception{
MssAuthenticationFilter filter = new MssAuthenticationFilter("/api/v1/login","id","password");
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationSuccessHandler(simpleTextAuthenticationSuccessHandler());
filter.setAuthenticationFailureHandler(simpleAuthenticationFailureHandler());
filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
return filter;
}
@Bean
GdprFilter gdprFilter() {
return new GdprFilter();
}
@Bean
public AuthenticationSuccessHandler simpleTextAuthenticationSuccessHandler() {
return new SimpleTextAuthenticationSuccessHandler();
}
@Bean
AuthenticationFailureHandler simpleAuthenticationFailureHandler() {
return new Http401AuthenticationFailureHandler();
}
@Bean
public LogoutSuccessHandler simpleLogoutSuccessHandler() {
return new HttpStatusReturningLogoutSuccessHandler();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public MssAuthenticationProvider mssAuthenticationProvider() {
return new MssAuthenticationProvider(mssUserDetailsService,passwordEncoder());
}
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(mssUserDetailsService)
.passwordEncoder(passwordEncoder())
.and().authenticationProvider(mssAuthenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception{
http.csrf()
.ignoringAntMatchers(GDC_TASK_API_URI)
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.addFilterBefore(mssAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(gdprFilter(), MssAuthenticationFilter.class)
.headers()
.frameOptions().disable()
.and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api/v1/login").permitAll()
.anyRequest().authenticated()
.and()
.logout()
.logoutUrl("/api/v1/logout")
.logoutSuccessHandler(simpleLogoutSuccessHandler())
.and()
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.sessionManagement()
.maximumSessions(1)
.sessionRegistry(sessionRegistry());
}
}
Authority.java
package com.liu.security;
public enum Authority {
NONE,
ALL,
DIVISION_GROUP,
DIVISION,
HEAD,
TEAM,
}
GdprFilter.java
package com.liu.security;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class GdprFilter implements Filter{
private static final String GDPR_RESOURCE_PATH="user-authority";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
if(!this.isGdprPath(request)) {
chain.doFilter(request, response);
return ;
}
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
private boolean isGdprPath(ServletRequest request) {
if(request instanceof HttpServletRequest) {
String url = ((HttpServletRequest)request).getRequestURL().toString();
return url.contains("/"+GDPR_RESOURCE_PATH);
}
return false;
}
private String getIp(ServletRequest request) {
String ip = ((HttpServletRequest)request).getHeader("X-FORWARDED-FOR");
if(ip == null) {
ip = request.getRemoteAddr();
}
return ip;
}
}
Http401AuthenticationFailureHandler.java
package com.liu.security;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
public class Http401AuthenticationFailureHandler implements AuthenticationFailureHandler{
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
// TODO Auto-generated method stub
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
PrintWriter writer = response.getWriter();
String detail = "error";
writer.println("{\n"+
"\"message\":\"error.http.401\"\n"+
",\"path\":\"/api/v1/account\"\n"+
",\"detail\":\""+detail+"\"\n"+
"}");
writer.flush();
}
}
MssAuthenticationFilter.java
package com.liu.security;
import java.io.IOException;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
public class MssAuthenticationFilter extends AbstractAuthenticationProcessingFilter{
private final String usernameParameterName;
private final String passwordParameterName;
private final ObjectMapper objectMapper;
public MssAuthenticationFilter(String defaultFilterProcessesUrl,String usernameParameterName,String passwordParameterName) {
super(defaultFilterProcessesUrl);
this.usernameParameterName = usernameParameterName;
this.passwordParameterName = passwordParameterName;
this.objectMapper = new ObjectMapper();
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws IOException{
MssAuthenticationToken token = null;
if(request.getContentType().contains(MediaType.APPLICATION_JSON.toString())) {
TypeReference<Map<String,String>> typeRef = new TypeReference<Map<String,String>>(){};
Map<String,String> loginVo = objectMapper.readValue(request.getReader(), typeRef);
token = new MssAuthenticationToken(
loginVo.get(usernameParameterName),
loginVo.get(passwordParameterName)
);
}else if(request.getContentType().contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
token = new MssAuthenticationToken(
request.getParameter(usernameParameterName),
request.getParameter(passwordParameterName)
);
}
return getAuthenticationManager().authenticate(token);
}
}