package com.jilongda.applet.config;
import com.jilongda.applet.security.AuthenticationProvider;
import com.jilongda.applet.security.SysUserDetailsService;
import com.jilongda.common.basic.Constant;
import com.jilongda.common.cache.CaffineCache;
import com.jilongda.common.redis.RedisAutoTemplate;
import com.jilongda.common.security.SecurityUtils;
import com.jilongda.common.security.filter.AuthenticationFilter;
import com.jilongda.common.security.hadler.SecurityAuthenticationEntryPoint;
import com.jilongda.common.swagger.SwaggerAutoConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* 细粒度的访问控制
*
* 注:需要使用注解@EnableGlobalMethodSecurity(prePostEnabled=true) 开启
*
* @author xiaochen
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SecurityAuthenticationEntryPoint securityAuthenticationEntryPoint;
@Autowired
private RedisAutoTemplate redisAutoTemplate;
@Autowired
private CaffineCache accessTokenCache;
@Autowired
private CaffineCache refreshTokenCache;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private SysUserDetailsService loadUserDetailsService;
/**
* 应用重启token无效,可手动使令牌无效,管理了token的生命周期
*
* @return
*/
@Bean
public SecurityUtils securityUtils() {
return new SecurityUtils(accessTokenCache, refreshTokenCache);
// return new SecurityUtils();
}
/**
* 不需要认证的在此处放行,比如系统的一些字典请求
* 会包含Basic认证的请求。意思是如果请求在Basic中存在,在此处也存在,那么该请求的认证会被忽略
* 这种方式的优势是不走 Spring Security 过滤器链
*
* @param web
*/
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(SwaggerAutoConfiguration.DOC_LIST);
web.ignoring().antMatchers(Constant.APPLET_AUTH_WHITELIST);
}
/**
* 使用自定义登录身份认证组件,自实现rest登录请求,不适用于在过滤其中实现 在过滤其中无法提供接口文档,维护不方便
*
* @param auth
*/
@Override
public void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(new AuthenticationProvider(loadUserDetailsService,passwordEncoder));
}
/**
* Security各项配置
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 需要Basic认证的请求
http
.cors().disable()
// 关闭登录,自定义接口实现
.formLogin().disable()
// 关闭httpBasic认证
.httpBasic().disable()
// 关闭退出,自定义接口实现
.logout().disable()
.csrf().disable()
// 放在 Cookie 中返回前端,防止跨域伪造
//.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
//.and()
.authorizeRequests()
// 跨域预检请求
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 登录URL permitAll() 无需保护 ---> 此种方式配置忽略认证规则会走Spring Security 过滤器链,在过滤器链中,给请求放行
// 不需要保护的请求,但需要经过过滤连
//.antMatchers(HttpMethod.POST, "/login").permitAll()
// 微信登录
.antMatchers(HttpMethod.GET, "/**/openId-by-jscode2session/**").permitAll()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.antMatchers(HttpMethod.POST, "/code/login").permitAll()
.antMatchers(HttpMethod.POST, "/sendCode").permitAll()
.antMatchers(HttpMethod.POST, "/sendPassCode").permitAll()
.antMatchers(HttpMethod.POST, "/pageResetPassword").permitAll()
// 其他都需要权限认证
.anyRequest()
.authenticated()
// 其他所有请求需要身份认证
.and()
// 异常拦截
.exceptionHandling()
// 未授权用户访问无权限时的处理
.authenticationEntryPoint(securityAuthenticationEntryPoint);
// 此处存在session共享的问题,如要共享session,则需要管理
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 访问控制时登录状态检查过滤器
http.addFilterBefore(new AuthenticationFilter(securityUtils()), UsernamePasswordAuthenticationFilter.class);
// 异常捕捉过滤器,必须在AuthenticationFilter之前才能捕捉到异常信息
http.addFilterBefore(new AuthenticationFilter(securityUtils()), AuthenticationFilter.class);
//禁用缓存
http.headers().cacheControl();
}
/**
* 验证管理器,在登录接口时用到
*
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}