package com.timeyang.amanda.config;

import com.timeyang.amanda.user.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
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;

/**
 * 安全配置
 *
 * @author chaokunyang
 * @create 2017-04-15
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
    prePostEnabled = true, order = 0, mode = AdviceMode.PROXY
)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Bean
    protected SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
        builder
                .userDetailsService(userService)
                .passwordEncoder(new BCryptPasswordEncoder())
                .and()
                .eraseCredentials(true);
    }

    @Override
    public void configure(WebSecurity security) throws Exception {
        security.ignoring().antMatchers("/assets/**", "/favicon.ico", "/built/**");
    }

    @Override
    protected void configure(HttpSecurity security) throws Exception {
        security
                .authorizeRequests() // authorize all requests using the access rules defined earlier
                .antMatchers("/session/list")
                .hasAuthority("VIEW_USER_SESSIONS")
                .anyRequest().authenticated()
                .and().formLogin()
                .loginPage("/login").failureUrl("/login?loginFailed")
                .defaultSuccessUrl("/admin")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and().logout()
                .logoutUrl("/logout").logoutSuccessUrl("/login?loggedOut")
                .invalidateHttpSession(true).deleteCookies("JSESSIONID")
                .permitAll()
                .and().sessionManagement()
                .sessionFixation().changeSessionId()
                .maximumSessions(1).maxSessionsPreventsLogin(true)
                .sessionRegistry(this.sessionRegistry())
                .and().and().csrf()
                .requireCsrfProtectionMatcher(request -> {
                    String m = request.getMethod();
                    return !request.getServletPath().startsWith("/api/") &&
                            ("POST".equals(m)) || "PUT".equals(m) ||
                            "DELETE".equals(m) || "PATCH".equals(m); // rest services 不用考虑跨站请求伪造
                });
    }
}
@@ -138,16 +138,93 @@
 owired;%0A
+import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;%0A
 import o
@@ -273,16 +273,16 @@
 ceMode;%0A
-
 import o
@@ -1233,16 +1233,91 @@
 Encoder;
+%0Aimport org.springframework.security.web.session.HttpSessionEventPublisher;
 %0A%0A/**%0A *
@@ -1629,16 +1629,239 @@
 rvice;%0A%0A
+    // https://github.com/spring-projects/spring-security/issues/3078%0A    // The SessionRegistry is not exposed as a Bean so there is no way for ApplicationEvent to be published to it. A workaround is to do something like:%0A
     @Bea
@@ -4051,15 +4051,557 @@
     %7D);%0A
-
     %7D%0A
+%0A    // missing HttpSessionEventPublisher in spring boot%E3%80%82Because SessionRegistry is not exposed as a Bean so there is no way for ApplicationEvent to be published to it, spring boot doesn't provide HttpSessionEventPublisher. solved by register both two%0A    // https://github.com/spring-projects/spring-boot/issues/1537%0A    // Register HttpSessionEventPublisher%0A    @Bean%0A    public static ServletListenerRegistrationBean httpSessionEventPublisher() %7B%0A        return new ServletListenerRegistrationBean(new HttpSessionEventPublisher());%0A    %7D%0A%0A
 %7D

解决spring security退出后不能再次登录的bug。 HttpSessionEventPublisher和SessionRegistry未注册