无关风月
2025-01-06 01788ea18a48b738df0807f656a4007a5f16a13a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package com.jilongda.manage.config;
 
import com.jilongda.common.security.ExceptionHandleFilter;
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;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
 
/**
 * 细粒度的访问控制
 * <p>
 * 注:需要使用注解@EnableGlobalMethodSecurity(prePostEnabled=true) 开启
 *
 * @author xiaochen
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    private final CaffineCache<String> accessTokenCache;
    private final CaffineCache<String> refreshTokenCache;
    private final PasswordEncoder passwordEncoder;
    private final SecurityAccessDeniedHandler securityAccessDeniedHandler;
    private final SecurityAuthenticationEntryPoint securityAuthenticationEntryPoint;
    private final SysUserDetailsService loadUserDetailsService;
 
 
    @Autowired
    public WebSecurityConfig(CaffineCache<String> accessTokenCache, CaffineCache<String> 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);
        // 异常捕捉过滤器,必须在AuthenticationFilter之前才能捕捉到异常信息
        http.addFilterBefore(new ExceptionHandleFilter(), AuthenticationFilter.class);
        //禁用缓存
        http.headers().cacheControl();
    }
 
 
    /**
     * 验证管理器,在登录接口时用到
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }
 
}