package mingzuozhibi.config;

import mingzuozhibi.persist.core.User;
import mingzuozhibi.persist.core.UserDao;
import mingzuozhibi.support.JsonArg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.*;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@RestController
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@PropertySource("file:config/setting.properties")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);

    @Autowired
    private UserDao userDao;

    @Value("${security.admin.password}")
    private String securityAdminPassword;

    @Value("${security.admin.userlist}")
    private String securityAdminUserlist;

    private MyUserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        setupAdminUser();
        userDetailsService = new MyUserDetailsService();
        auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/api/users/**").hasRole("ADMIN")
                .antMatchers(HttpMethod.GET).permitAll()
                .antMatchers("/api/login").permitAll()
                .antMatchers("/api/**").hasRole("USER");
        http.httpBasic();
        http.csrf().disable();

        logger.info("设置安全策略");
    }

    @PostMapping("/api/login")
    public String ajaxLogin(@JsonArg("$.username") String username,
                            @JsonArg("$.password") String password) {
        logger.info("用户登入: username={}, password=******", username);
        try {
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            if (userDetails.getPassword().equals(password) && userDetails.isEnabled()) {
                Authentication authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, userDetails.getPassword(), userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authentication);
                return "login success!";
            }
        } catch (UsernameNotFoundException ignored) {
        }
        return "login failed!";
    }

    private void setupAdminUser() {
        Logger logger = LoggerFactory.getLogger(this.getClass());

        User user = userDao.findByUsername("admin");
        if (user == null) {
            user = new User();
            user.setUsername("admin");
            user.setPassword(securityAdminPassword);
            userDao.save(user);

            logger.info("创建管理员用户");
        } else if (!securityAdminPassword.equals(user.getPassword())) {
            user.setPassword(securityAdminPassword);
            userDao.save(user);

            logger.info("更新管理员密码");
        }
    }


    private class MyUserDetailsService implements UserDetailsService {

        private final List<GrantedAuthority> ADMIN_ROLES;
        private final List<GrantedAuthority> USER_ROLES;
        private final HashSet<String> ADMIN_LIST;

        private MyUserDetailsService() {
            ADMIN_ROLES = Stream.of("ROLE_USER", "ROLE_ADMIN")
                    .map(SimpleGrantedAuthority::new)
                    .collect(Collectors.toList());
            USER_ROLES = Stream.of("ROLE_USER")
                    .map(SimpleGrantedAuthority::new)
                    .collect(Collectors.toList());
            ADMIN_LIST = Stream.of(securityAdminUserlist.split(","))
                    .collect(Collectors.toCollection(HashSet::new));
        }

        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            User user = userDao.findByUsername(username);
            if (user == null) {
                throw new UsernameNotFoundException("username " + username + " not found");
            }
            return new UserDetails() {
                public List<GrantedAuthority> getAuthorities() {
                    onLoginSuccess();
                    return ADMIN_LIST.contains(username) ? ADMIN_ROLES : USER_ROLES;
                }

                private void onLoginSuccess() {
                    user.setLastLoggedin(new Date());
                    userDao.save(user);
                }

                public String getPassword() {
                    return user.getPassword();
                }

                public String getUsername() {
                    return user.getUsername();
                }

                public boolean isEnabled() {
                    return user.isEnabled();
                }

                public boolean isAccountNonLocked() {
                    return true;
                }

                public boolean isAccountNonExpired() {
                    return true;
                }

                public boolean isCredentialsNonExpired() {
                    return true;
                }
            };
        }

    }
}

@@ -1426,17 +1426,90 @@
 otation.
-*
+PostMapping;%0Aimport org.springframework.web.bind.annotation.RestController
 ;%0A%0Aimpor
@@ -3568,140 +3568,374 @@
     
-return %22login success!%22;%0A            %7D%0A        %7D catch (UsernameNotFoundException ignored) %7B%0A        %7D%0A        return %22login failed!
+logger.info(%22%E7%94%A8%E6%88%B7%E7%99%BB%E5%85%A5: %E7%94%A8%E6%88%B7%E5%B7%B2%E6%88%90%E5%8A%9F%E7%99%BB%E5%85%A5, username=%7B%7D%22, username);%0A                return %22%7B%5C%22success%5C%22: true%7D%22;%0A            %7D else %7B%0A                logger.info(%22%E7%94%A8%E6%88%B7%E7%99%BB%E5%85%A5: %E6%9C%AA%E8%83%BD%E6%88%90%E5%8A%9F%E7%99%BB%E5%85%A5, username=%7B%7D%22, username);%0A            %7D%0A        %7D catch (UsernameNotFoundException ignored) %7B%0A            logger.info(%22%E7%94%A8%E6%88%B7%E7%99%BB%E5%85%A5: %E6%9C%AA%E6%89%BE%E5%88%B0%E8%AF%A5%E7%94%A8%E6%88%B7, username=%7B%7D%22, username);%0A        %7D%0A        return %22%7B%5C%22success%5C%22: false%7D
 %22;%0A 

add log && return json