@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Profile("prod")
@Bean
public FilterRegistrationBean<JwtAuthenticationFilter> jwtAuthenticationFilterBean() {
final FilterRegistrationBean<JwtAuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(jwtAuthenticationFilter());
registrationBean.addUrlPatterns(RecruitAction.BASE_MAPPING);
registrationBean.addUrlPatterns(AgentAction.BASE_MAPPING);
registrationBean.addUrlPatterns(StaffAction.BASE_MAPPING);
registrationBean.addUrlPatterns(RecruitAction.BASE_MAPPING + "/*");
registrationBean.addUrlPatterns(AgentAction.BASE_MAPPING + "/*");
registrationBean.addUrlPatterns(StaffAction.BASE_MAPPING + "/*");
return registrationBean;
}
@Profile("prod")
@Bean(name = "jwtAuthenticationFilter")
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Profile("!prod")
@Bean
public FilterRegistrationBean<MockJwtAuthenticationFilter> mockJwtAuthenticationFilterBean() {
final FilterRegistrationBean<MockJwtAuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(mockJwtAuthenticationFilter());
registrationBean.addUrlPatterns(RecruitAction.BASE_MAPPING);
registrationBean.addUrlPatterns(AgentAction.BASE_MAPPING);
registrationBean.addUrlPatterns(StaffAction.BASE_MAPPING);
registrationBean.addUrlPatterns(RecruitAction.BASE_MAPPING + "/*");
registrationBean.addUrlPatterns(AgentAction.BASE_MAPPING + "/*");
registrationBean.addUrlPatterns(StaffAction.BASE_MAPPING + "/*");
return registrationBean;
}
@Profile("!prod")
@Bean(name = "mockJwtAuthenticationFilter")
public MockJwtAuthenticationFilter mockJwtAuthenticationFilter() {
return new MockJwtAuthenticationFilter();
}
@Override
protected void configure(final HttpSecurity httpSecurity) throws Exception {
// 取消預設登入畫面
httpSecurity
.httpBasic().disable()
.csrf().disable();
httpSecurity
.authorizeRequests()
.antMatchers("/recruit", "/agent", "/staff", "/**/logout").permitAll();
//2025.09.01不安全標頭處理
String nonce = NonceGenerator.generateNonce();
httpSecurity.headers().contentSecurityPolicy("script-src 'self' 'nonce-"+nonce+"' ; object-src 'self' ; frame-ancestors 'self'");
httpSecurity.headers().referrerPolicy().policy(ReferrerPolicy.NO_REFERRER);
httpSecurity.headers().addHeaderWriter(new StaticHeadersWriter("Permissions-Policy", "geolocation=(),midi=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self)"));
}
QENvbmZpZ3VyYXRpb24KQEVuYWJsZVdlYlNlY3VyaXR5CnB1YmxpYyBjbGFzcyBXZWJTZWN1cml0eUNvbmZpZyBleHRlbmRzIFdlYlNlY3VyaXR5Q29uZmlndXJlckFkYXB0ZXIgewoKCUBQcm9maWxlKCJwcm9kIikKCUBCZWFuCglwdWJsaWMgRmlsdGVyUmVnaXN0cmF0aW9uQmVhbjxKd3RBdXRoZW50aWNhdGlvbkZpbHRlcj4gand0QXV0aGVudGljYXRpb25GaWx0ZXJCZWFuKCkgewoJCWZpbmFsIEZpbHRlclJlZ2lzdHJhdGlvbkJlYW48Snd0QXV0aGVudGljYXRpb25GaWx0ZXI+IHJlZ2lzdHJhdGlvbkJlYW4gPSBuZXcgRmlsdGVyUmVnaXN0cmF0aW9uQmVhbjw+KCk7CgkJcmVnaXN0cmF0aW9uQmVhbi5zZXRGaWx0ZXIoand0QXV0aGVudGljYXRpb25GaWx0ZXIoKSk7CgkJcmVnaXN0cmF0aW9uQmVhbi5hZGRVcmxQYXR0ZXJucyhSZWNydWl0QWN0aW9uLkJBU0VfTUFQUElORyk7CgkJcmVnaXN0cmF0aW9uQmVhbi5hZGRVcmxQYXR0ZXJucyhBZ2VudEFjdGlvbi5CQVNFX01BUFBJTkcpOwoJCXJlZ2lzdHJhdGlvbkJlYW4uYWRkVXJsUGF0dGVybnMoU3RhZmZBY3Rpb24uQkFTRV9NQVBQSU5HKTsKCQlyZWdpc3RyYXRpb25CZWFuLmFkZFVybFBhdHRlcm5zKFJlY3J1aXRBY3Rpb24uQkFTRV9NQVBQSU5HICsgIi8qIik7CgkJcmVnaXN0cmF0aW9uQmVhbi5hZGRVcmxQYXR0ZXJucyhBZ2VudEFjdGlvbi5CQVNFX01BUFBJTkcgKyAiLyoiKTsKCQlyZWdpc3RyYXRpb25CZWFuLmFkZFVybFBhdHRlcm5zKFN0YWZmQWN0aW9uLkJBU0VfTUFQUElORyArICIvKiIpOwoJCXJldHVybiByZWdpc3RyYXRpb25CZWFuOwoJfQoKCUBQcm9maWxlKCJwcm9kIikKCUBCZWFuKG5hbWUgPSAiand0QXV0aGVudGljYXRpb25GaWx0ZXIiKQoJcHVibGljIEp3dEF1dGhlbnRpY2F0aW9uRmlsdGVyIGp3dEF1dGhlbnRpY2F0aW9uRmlsdGVyKCkgewoJCXJldHVybiBuZXcgSnd0QXV0aGVudGljYXRpb25GaWx0ZXIoKTsKCX0KCglAUHJvZmlsZSgiIXByb2QiKQoJQEJlYW4KCXB1YmxpYyBGaWx0ZXJSZWdpc3RyYXRpb25CZWFuPE1vY2tKd3RBdXRoZW50aWNhdGlvbkZpbHRlcj4gbW9ja0p3dEF1dGhlbnRpY2F0aW9uRmlsdGVyQmVhbigpIHsKCQlmaW5hbCBGaWx0ZXJSZWdpc3RyYXRpb25CZWFuPE1vY2tKd3RBdXRoZW50aWNhdGlvbkZpbHRlcj4gcmVnaXN0cmF0aW9uQmVhbiA9IG5ldyBGaWx0ZXJSZWdpc3RyYXRpb25CZWFuPD4oKTsKCQlyZWdpc3RyYXRpb25CZWFuLnNldEZpbHRlcihtb2NrSnd0QXV0aGVudGljYXRpb25GaWx0ZXIoKSk7CgkJcmVnaXN0cmF0aW9uQmVhbi5hZGRVcmxQYXR0ZXJucyhSZWNydWl0QWN0aW9uLkJBU0VfTUFQUElORyk7CgkJcmVnaXN0cmF0aW9uQmVhbi5hZGRVcmxQYXR0ZXJucyhBZ2VudEFjdGlvbi5CQVNFX01BUFBJTkcpOwoJCXJlZ2lzdHJhdGlvbkJlYW4uYWRkVXJsUGF0dGVybnMoU3RhZmZBY3Rpb24uQkFTRV9NQVBQSU5HKTsKCQlyZWdpc3RyYXRpb25CZWFuLmFkZFVybFBhdHRlcm5zKFJlY3J1aXRBY3Rpb24uQkFTRV9NQVBQSU5HICsgIi8qIik7CgkJcmVnaXN0cmF0aW9uQmVhbi5hZGRVcmxQYXR0ZXJucyhBZ2VudEFjdGlvbi5CQVNFX01BUFBJTkcgKyAiLyoiKTsKCQlyZWdpc3RyYXRpb25CZWFuLmFkZFVybFBhdHRlcm5zKFN0YWZmQWN0aW9uLkJBU0VfTUFQUElORyArICIvKiIpOwoJCXJldHVybiByZWdpc3RyYXRpb25CZWFuOwoJfQoKCUBQcm9maWxlKCIhcHJvZCIpCglAQmVhbihuYW1lID0gIm1vY2tKd3RBdXRoZW50aWNhdGlvbkZpbHRlciIpCglwdWJsaWMgTW9ja0p3dEF1dGhlbnRpY2F0aW9uRmlsdGVyIG1vY2tKd3RBdXRoZW50aWNhdGlvbkZpbHRlcigpIHsKCQlyZXR1cm4gbmV3IE1vY2tKd3RBdXRoZW50aWNhdGlvbkZpbHRlcigpOwoJfQoKCUBPdmVycmlkZQoJcHJvdGVjdGVkIHZvaWQgY29uZmlndXJlKGZpbmFsIEh0dHBTZWN1cml0eSBodHRwU2VjdXJpdHkpIHRocm93cyBFeGNlcHRpb24gewoJCS8vIOWPlua2iOmgkOioreeZu+WFpeeVq+mdogoJCWh0dHBTZWN1cml0eQoJCQkuaHR0cEJhc2ljKCkuZGlzYWJsZSgpCgkJCS5jc3JmKCkuZGlzYWJsZSgpOwoJCWh0dHBTZWN1cml0eQoJCQkuYXV0aG9yaXplUmVxdWVzdHMoKQoJCQkuYW50TWF0Y2hlcnMoIi9yZWNydWl0IiwgIi9hZ2VudCIsICIvc3RhZmYiLCAiLyoqL2xvZ291dCIpLnBlcm1pdEFsbCgpOwoJCS8vMjAyNS4wOS4wMeS4jeWuieWFqOaomemgreiZleeQhgoJCVN0cmluZyBub25jZSA9IE5vbmNlR2VuZXJhdG9yLmdlbmVyYXRlTm9uY2UoKTsKCQlodHRwU2VjdXJpdHkuaGVhZGVycygpLmNvbnRlbnRTZWN1cml0eVBvbGljeSgic2NyaXB0LXNyYyAnc2VsZicgJ25vbmNlLSIrbm9uY2UrIicgOyBvYmplY3Qtc3JjICdzZWxmJyA7IGZyYW1lLWFuY2VzdG9ycyAnc2VsZiciKTsKCQlodHRwU2VjdXJpdHkuaGVhZGVycygpLnJlZmVycmVyUG9saWN5KCkucG9saWN5KFJlZmVycmVyUG9saWN5Lk5PX1JFRkVSUkVSKTsKCQlodHRwU2VjdXJpdHkuaGVhZGVycygpLmFkZEhlYWRlcldyaXRlcihuZXcgU3RhdGljSGVhZGVyc1dyaXRlcigiUGVybWlzc2lvbnMtUG9saWN5IiwgImdlb2xvY2F0aW9uPSgpLG1pZGk9KCksbWljcm9waG9uZT0oKSxjYW1lcmE9KCksbWFnbmV0b21ldGVyPSgpLGd5cm9zY29wZT0oKSxmdWxsc2NyZWVuPShzZWxmKSIpKTsKCQkKCX0K