我的应用程序注销不起作用。 当我点击注销按钮时,如果确认注销,我会保持相同的连接->我不需要再次登录即可访问资源,我可以直接访问资源页面。 注销确认窗口不会出现,如果我写 /logout:
但我总是可以再次访问资源而无需登录。 在浏览器控制台中,我有这个:
客户的安全配置:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
private final ClientRegistrationRepository clientRegistrationRepository;
public SecurityConfig(ClientRegistrationRepository clientRegistrationRepository){
this.clientRegistrationRepository=clientRegistrationRepository;
}
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(Customizer.withDefaults())//config par def.
.authorizeHttpRequests(ar-> ar.anyRequest().authenticated())
.headers(h->h.frameOptions(fo->fo.disable()))
.csrf(crsf->crsf.ignoringRequestMatchers("/h2-console/**"))
.authorizeHttpRequests(ar->ar.requestMatchers("/","/webjars/**","/h2-console/**", "/oauth2Login/**").permitAll())
.oauth2Login(al->al.loginPage("/oauth2Login").defaultSuccessUrl("/"))
.logout((logout)->logout
.logoutSuccessHandler(oidcLogoutSuccessHandler())
.logoutSuccessUrl("/").permitAll()
.clearAuthentication(true)
.deleteCookies("JSESSIONID"))
.exceptionHandling(eh->eh.accessDeniedPage("/notAuthorized"));
return http.build();
}
private OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler() {
final OidcClientInitiatedLogoutSuccessHandler oidcClientInitiatedLogoutSuccessHandler=
new OidcClientInitiatedLogoutSuccessHandler(this.clientRegistrationRepository);
oidcClientInitiatedLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}?logoutsuccess=true");
return oidcClientInitiatedLogoutSuccessHandler;
}
@Bean
public GrantedAuthoritiesMapper userAuthoritiesMapper() {
return (authorities -> {
final Set<GrantedAuthority> mappedAuthorities=new HashSet<>();
authorities.forEach((authority)->{
if (authority instanceof OidcUserAuthority oidcAuth){
mappedAuthorities.addAll(mappedAuthorities(oidcAuth.getIdToken().getClaims()));
System.out.println(oidcAuth.getAttributes());
} else if (authority instanceof OAuth2UserAuthority oauth2Auth) {
mappedAuthorities.addAll(mappedAuthorities(oauth2Auth.getAttributes()));
}
});
return mappedAuthorities;
});
}
private List<SimpleGrantedAuthority> mappedAuthorities(final Map<String, Object> attributes) {
final Map<String, Object> realmAccess= (Map<String, Object>) attributes.getOrDefault("realm_access", Collections.emptyMap());
final Collection<String> roles=((Collection<String>)realmAccess.getOrDefault("roles", Collections.emptyList()));
return roles.stream()
.map(SimpleGrantedAuthority::new)
.toList();
}
控制器:
@Controller
public class CustomerController {
/**
* acces a interface
*/
private CustomerRepository customerRepository;
private ClientRegistrationRepository clientRegistrationRepository;
public CustomerController(CustomerRepository customerRepository, ClientRegistrationRepository registrationRepository) {
this.customerRepository = customerRepository;
this.clientRegistrationRepository = clientRegistrationRepository;
}
@GetMapping("/customers")
@PreAuthorize("hasAuthority('ADMIN')")
public String customers(Model model){
List<Customer> customersList = customerRepository.findAll();
model.addAttribute("customers", customersList);
return "customers";//return attributeName du model=CustomerList
}
@GetMapping("/products")
public String products(Model model){
SecurityContext context= SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
OAuth2AuthenticationToken oAuth2AuthenticationToken = (OAuth2AuthenticationToken) authentication;
DefaultOidcUser oidcUser= (DefaultOidcUser) oAuth2AuthenticationToken.getPrincipal();
String jwtTokenValue=oidcUser.getIdToken().getTokenValue();
RestClient restClient= RestClient.create("http://localhost:8085");
List<Product> products=restClient.get()
.uri("/products")
.headers(httpHeaders ->httpHeaders.set(HttpHeaders.AUTHORIZATION,"Bearer"+"jwtTokenValue"))
.retrieve()
.body(new ParameterizedTypeReference<>() {});
model.addAttribute("products", products);
return "products";
}
@GetMapping("/auth")
@ResponseBody
public Authentication auth(Authentication authentication){
return authentication;
}
@GetMapping("/")
public String index(){
return "index";
}
@GetMapping("/notAuthorized")
public String notAuthorized(){
return "notAuthorized";
}
@GetMapping("/oauth2Login")
public String oauth2Login(Model model){
String authorizationRequestBaseUri="oauth2/authorization";
Map<String, String> oauth2AuthenticationUrls=new HashMap();
Iterable<ClientRegistration> clientRegistrations=(Iterable<ClientRegistration>)clientRegistrationRepository;
clientRegistrations.forEach(clientRegistration->{
oauth2AuthenticationUrls.put(clientRegistration.getClientName(),
authorizationRequestBaseUri+"/"+ clientRegistration.getRegistrationId());
});
model.addAttribute("urls",oauth2AuthenticationUrls);
return "oauth2Login";
}
感谢您的帮助。
对
/logout
的请求应该是POST
(这意味着CSRF保护将出现在游戏中),并且当您配置OIDC注销成功处理程序时,重定向应该是到授权服务器。这里您被重定向到登录,因为注销被拒绝(错误的 HTTP 动词)。
一旦修复了 HTTP 谓词(发送
POST
请求而不是 GET
),您可能会面临 CSRF 问题,因为您正在使用 SPA 并且没有为此类应用程序配置 CSRF 保护...