liujie
2026-09-14 82a8bc62d5b9d5d607f6952329952b17eda2f0a0
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package com.tourguide.config;
 
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tourguide.common.api.ApiResponse;
import com.tourguide.security.JwtAuthenticationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
 
@Configuration
public class SecurityConfig {
    @Bean
    public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
 
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtFilter, ObjectMapper objectMapper)
            throws Exception {
        http.csrf().disable()
                .cors().and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .exceptionHandling()
                .authenticationEntryPoint((request, response, exception) -> {
                    response.setStatus(401);
                    response.setCharacterEncoding(StandardCharsets.UTF_8.name());
                    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                    objectMapper.writeValue(response.getWriter(), ApiResponse.failure(401, "请先登录"));
                })
                .accessDeniedHandler((request, response, exception) -> {
                    response.setStatus(403);
                    response.setCharacterEncoding(StandardCharsets.UTF_8.name());
                    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                    objectMapper.writeValue(response.getWriter(), ApiResponse.failure(403, "无权访问"));
                }).and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/api/admin/auth/login", "/api/mini/auth/wechat-phone-login",
                        "/api/guide/auth/login", "/api/guide/auth/password-login", "/api/guide/auth/sms-code",
                        "/api/health").permitAll()
                .antMatchers("/api/guide/registration/**").permitAll()
                .antMatchers("/base/dougong/**", "/api/base/dougong/**").permitAll()
                .antMatchers(HttpMethod.GET, "/uploads/avatars/**").permitAll()
                .antMatchers(HttpMethod.GET, "/uploads/review-images/**").permitAll()
                .antMatchers("/uploads/guide-applications/**")
                    .hasAnyAuthority("PERM_GUIDE_LIST_VIEW", "PERM_GUIDE_AUDIT_VIEW")
                .antMatchers(HttpMethod.GET, "/api/mini/scenic-spots", "/api/mini/scenic-spots/**").permitAll()
                .antMatchers(HttpMethod.GET, "/api/mini/agreements", "/api/mini/agreements/**").permitAll()
                .antMatchers(HttpMethod.GET, "/api/guide/agreements", "/api/guide/agreements/**").permitAll()
                .antMatchers("/api/admin/auth/me", "/api/admin/auth/logout").hasRole("ADMIN")
                .antMatchers(HttpMethod.GET, "/api/admin/dashboard/**")
                    .hasAuthority("PERM_DASHBOARD_VIEW")
                .antMatchers(HttpMethod.PUT, "/api/admin/mini-users/*/status")
                    .hasAuthority("PERM_VISITOR_LIST_STATUS")
                .antMatchers(HttpMethod.GET, "/api/admin/mini-users/**")
                    .hasAuthority("PERM_VISITOR_LIST_VIEW")
                .antMatchers("/api/admin/mini-users/**").hasAuthority("PERM_VISITOR_LIST")
                .antMatchers(HttpMethod.POST, "/api/admin/guide-users")
                    .hasAuthority("PERM_GUIDE_LIST_ADD")
                .antMatchers(HttpMethod.PUT, "/api/admin/guide-users/*/status")
                    .hasAuthority("PERM_GUIDE_LIST_STATUS")
                .antMatchers(HttpMethod.PUT, "/api/admin/guide-users/**")
                    .hasAuthority("PERM_GUIDE_LIST_EDIT")
                .antMatchers(HttpMethod.POST, "/api/admin/guide-users/**")
                    .hasAuthority("PERM_GUIDE_LIST_EDIT")
                .antMatchers(HttpMethod.DELETE, "/api/admin/guide-users/**")
                    .hasAuthority("PERM_GUIDE_LIST_EDIT")
                .antMatchers(HttpMethod.GET, "/api/admin/guide-users/**")
                    .hasAuthority("PERM_GUIDE_LIST_VIEW")
                .antMatchers("/api/admin/guide-users/**").hasAuthority("PERM_GUIDE_LIST")
                .antMatchers(HttpMethod.PUT, "/api/admin/guide-applications/*/audit")
                    .hasAuthority("PERM_GUIDE_AUDIT_AUDIT")
                .antMatchers(HttpMethod.GET, "/api/admin/guide-applications/**")
                    .hasAuthority("PERM_GUIDE_AUDIT_VIEW")
                .antMatchers("/api/admin/guide-applications/**").hasAuthority("PERM_GUIDE_AUDIT")
                .antMatchers(HttpMethod.POST, "/api/admin/guide-teams")
                    .hasAuthority("PERM_GUIDE_TEAM_ADD")
                .antMatchers(HttpMethod.PUT, "/api/admin/guide-teams/*/status")
                    .hasAuthority("PERM_GUIDE_TEAM_STATUS")
                .antMatchers(HttpMethod.PUT, "/api/admin/guide-teams/**")
                    .hasAuthority("PERM_GUIDE_TEAM_EDIT")
                .antMatchers(HttpMethod.DELETE, "/api/admin/guide-teams/**")
                    .hasAuthority("PERM_GUIDE_TEAM_DELETE")
                .antMatchers(HttpMethod.GET, "/api/admin/guide-teams/**")
                    .hasAuthority("PERM_GUIDE_TEAM_VIEW")
                .antMatchers("/api/admin/guide-teams/**").hasAuthority("PERM_GUIDE_TEAM")
                .antMatchers(HttpMethod.PUT, "/api/admin/orders/*/refund")
                    .hasAuthority("PERM_ORDER_LIST_REFUND")
                .antMatchers(HttpMethod.GET, "/api/admin/orders/**")
                    .hasAuthority("PERM_ORDER_LIST_VIEW")
                .antMatchers("/api/admin/orders/**").hasAuthority("PERM_ORDER_LIST")
                .antMatchers(HttpMethod.POST, "/api/admin/operation-cities")
                    .hasAuthority("PERM_CITY_LIST_ADD")
                .antMatchers(HttpMethod.PUT, "/api/admin/operation-cities/*/status")
                    .hasAuthority("PERM_CITY_LIST_STATUS")
                .antMatchers(HttpMethod.GET, "/api/admin/operation-cities/**")
                    .hasAuthority("PERM_CITY_LIST_VIEW")
                .antMatchers("/api/admin/operation-cities/**").hasAuthority("PERM_CITY_LIST")
                .antMatchers(HttpMethod.POST, "/api/admin/operation-scenic-spots")
                    .hasAuthority("PERM_SCENIC_LIST_ADD")
                .antMatchers(HttpMethod.PUT, "/api/admin/operation-scenic-spots/*/status")
                    .hasAuthority("PERM_SCENIC_LIST_STATUS")
                .antMatchers(HttpMethod.PUT, "/api/admin/operation-scenic-spots/**")
                    .hasAuthority("PERM_SCENIC_LIST_EDIT")
                .antMatchers(HttpMethod.DELETE, "/api/admin/operation-scenic-spots/**")
                    .hasAuthority("PERM_SCENIC_LIST_DELETE")
                .antMatchers(HttpMethod.GET, "/api/admin/operation-scenic-spots/**")
                    .hasAuthority("PERM_SCENIC_LIST_VIEW")
                .antMatchers("/api/admin/operation-scenic-spots/**").hasAuthority("PERM_SCENIC_LIST")
                .antMatchers(HttpMethod.DELETE, "/api/admin/reviews/**")
                    .hasAuthority("PERM_REVIEW_LIST_DELETE")
                .antMatchers(HttpMethod.GET, "/api/admin/reviews/**")
                    .hasAuthority("PERM_REVIEW_LIST_VIEW")
                .antMatchers("/api/admin/reviews/**").hasAuthority("PERM_REVIEW_LIST")
                .antMatchers(HttpMethod.PUT, "/api/admin/feedback/**")
                    .hasAuthority("PERM_FEEDBACK_LIST_EDIT")
                .antMatchers(HttpMethod.GET, "/api/admin/feedback/**")
                    .hasAuthority("PERM_FEEDBACK_LIST_VIEW")
                .antMatchers("/api/admin/feedback/**").hasAuthority("PERM_FEEDBACK_LIST")
                .antMatchers(HttpMethod.PUT, "/api/admin/platform-settings/**")
                    .hasAuthority("PERM_PLATFORM_SETTINGS_EDIT")
                .antMatchers(HttpMethod.GET, "/api/admin/platform-settings/**")
                    .hasAuthority("PERM_PLATFORM_SETTINGS_VIEW")
                .antMatchers("/api/admin/platform-settings/**").hasAuthority("PERM_PLATFORM_SETTINGS")
                .antMatchers(HttpMethod.PUT, "/api/admin/agreements/**")
                    .hasAuthority("PERM_AGREEMENT_SETTINGS_EDIT")
                .antMatchers(HttpMethod.GET, "/api/admin/agreements/**")
                    .hasAuthority("PERM_AGREEMENT_SETTINGS_VIEW")
                .antMatchers("/api/admin/agreements/**").hasAuthority("PERM_AGREEMENT_SETTINGS")
                .antMatchers("/api/admin/finance/ledger/**")
                    .hasAnyAuthority("PERM_FINANCE_DETAIL_VIEW", "PERM_FINANCE_REFUND_VIEW")
                .antMatchers(HttpMethod.PUT, "/api/admin/withdrawals/*/process")
                    .hasAuthority("PERM_WITHDRAWAL_LIST_AUDIT")
                .antMatchers(HttpMethod.GET, "/api/admin/withdrawals/**")
                    .hasAuthority("PERM_WITHDRAWAL_LIST_VIEW")
                .antMatchers("/api/admin/withdrawals/**").hasAuthority("PERM_WITHDRAWAL_LIST")
                .antMatchers(HttpMethod.POST, "/api/admin/roles")
                    .hasAuthority("PERM_ROLE_LIST_ADD")
                .antMatchers(HttpMethod.PUT, "/api/admin/roles/**")
                    .hasAuthority("PERM_ROLE_LIST_EDIT")
                .antMatchers(HttpMethod.DELETE, "/api/admin/roles/**")
                    .hasAuthority("PERM_ROLE_LIST_DELETE")
                .antMatchers(HttpMethod.GET, "/api/admin/roles/**")
                    .hasAuthority("PERM_ROLE_LIST_VIEW")
                .antMatchers("/api/admin/roles/**").hasAuthority("PERM_ROLE_LIST")
                .antMatchers(HttpMethod.POST, "/api/admin/users")
                    .hasAuthority("PERM_EMPLOYEE_LIST_ADD")
                .antMatchers(HttpMethod.PUT, "/api/admin/users/*/status")
                    .hasAuthority("PERM_EMPLOYEE_LIST_STATUS")
                .antMatchers(HttpMethod.PUT, "/api/admin/users/*/password")
                    .hasAuthority("PERM_EMPLOYEE_LIST_RESET_PASSWORD")
                .antMatchers(HttpMethod.PUT, "/api/admin/users/**")
                    .hasAuthority("PERM_EMPLOYEE_LIST_EDIT")
                .antMatchers(HttpMethod.DELETE, "/api/admin/users/**")
                    .hasAuthority("PERM_EMPLOYEE_LIST_DELETE")
                .antMatchers(HttpMethod.GET, "/api/admin/users/**")
                    .hasAuthority("PERM_EMPLOYEE_LIST_VIEW")
                .antMatchers("/api/admin/users/**").hasAuthority("PERM_EMPLOYEE_LIST")
                .antMatchers("/api/admin/**").denyAll()
                .antMatchers("/api/mini/**").hasRole("MINI_USER")
                .antMatchers("/api/guide/**").hasRole("GUIDE_USER")
                .anyRequest().authenticated().and()
                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
 
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOriginPatterns(Collections.singletonList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Collections.singletonList("*"));
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}