package com.jilongda.manage.config; import com.jilongda.manage.security.SecurityAccessDeniedHandler; import com.jilongda.manage.security.SysUserDetailsService; import com.jilongda.common.basic.Constant; import com.jilongda.common.cache.CaffineCache; import com.jilongda.common.security.SecurityUtils; import com.jilongda.common.security.filter.AuthenticationFilter; import com.jilongda.common.security.hadler.SecurityAuthenticationEntryPoint; import com.jilongda.manage.security.AuthenticationProvider; 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 { private final CaffineCache accessTokenCache; private final CaffineCache refreshTokenCache; private final PasswordEncoder passwordEncoder; private final SecurityAccessDeniedHandler securityAccessDeniedHandler; private final SecurityAuthenticationEntryPoint securityAuthenticationEntryPoint; private final SysUserDetailsService loadUserDetailsService; @Autowired public WebSecurityConfig(CaffineCache accessTokenCache, CaffineCache refreshTokenCache, PasswordEncoder passwordEncoder, SecurityAccessDeniedHandler securityAccessDeniedHandler, SecurityAuthenticationEntryPoint securityAuthenticationEntryPoint, SysUserDetailsService loadUserDetailsService) { this.accessTokenCache = accessTokenCache; this.refreshTokenCache = refreshTokenCache; this.passwordEncoder = passwordEncoder; this.securityAccessDeniedHandler = securityAccessDeniedHandler; this.securityAuthenticationEntryPoint = securityAuthenticationEntryPoint; this.loadUserDetailsService = 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(Constant.DOC_LIST); web.ignoring().antMatchers(Constant.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, "/**").permitAll() // 其他都需要权限认证 .anyRequest() .authenticated() // RBAC 动态 url 认证 //.access("@rbacAuthorityService.hasPermission(request,authentication)") // 其他所有请求需要身份认证 .and() // 异常拦截 .exceptionHandling() // 认证过的用户访问没有权限资源的处理 .accessDeniedHandler(securityAccessDeniedHandler) // 未授权用户访问无权限时的处理 .authenticationEntryPoint(securityAuthenticationEntryPoint) .and() .sessionManagement() .maximumSessions(5); // 此处存在session共享的问题,如要共享session,则需要管理 http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 访问控制时登录状态检查过滤器 http.addFilterBefore(new AuthenticationFilter(securityUtils()), UsernamePasswordAuthenticationFilter.class); //禁用缓存 http.headers().cacheControl(); } /** * 验证管理器,在登录接口时用到 * * @return * @throws Exception */ @Bean @Override public AuthenticationManager authenticationManager() throws Exception { return super.authenticationManager(); } }