我正在使用示例 Angular 应用程序来测试 Okta 与 springboot 应用程序的集成。 前端示例:https://github.com/okta-samples/okta-angular-sample
我有一个带有控制器的 springboot 微服务并启用了 spring security。我可以使用邮递员到达端点,没有任何问题,但是当通过角度发出请求时,我得到 401 Unauthorized 以下是服务器日志:
2024-01-07 19:19:18.499 DEBUG 10476 --- [nio-8040-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.4g.account.controller.AccountController#getOrders()
2024-01-07 19:19:18.511 DEBUG 10476 --- [nio-8040-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2024-01-07 19:19:18.520 DEBUG 10476 --- [nio-8040-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor : Failed to authorize filter invocation [POST /account/orders] with attributes [authenticated]
2024-01-07 19:19:18.524 DEBUG 10476 --- [nio-8040-exec-1] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2024-01-07 19:19:18.524 DEBUG 10476 --- [nio-8040-exec-1] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
profile.component.ts
import {HttpClient} from '@angular/common/http';
import { Component, Inject, OnInit } from '@angular/core';
import { IDToken, OktaAuth } from '@okta/okta-auth-js';
import { OKTA_AUTH } from '@okta/okta-angular';
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
orders!: { name: string; value: unknown }[];
constructor(@Inject(OKTA_AUTH) public oktaAuth: OktaAuth,private http: HttpClient) {
}
async ngOnInit() {
await this.getOrders();
}
async getOrders() {
const accessToken: AccessToken = await this.oktaAuth.tokenManager.get('accessToken') as AccessToken;
const apiUrl = 'http://localhost:8090/account/orders';
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken.accessToken}`
};
return this.http.post(apiUrl, { headers }).subscribe(data => {
this.orders = Object.entries(data).map(entry => ({ name: entry[0], value: entry[1] }));
})
}
}
安全配置.java
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
Environment environment;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().oauth2ResourceServer()
.authenticationManagerResolver(new JwtIssuerAuthenticationManagerResolver("https://xxxx.okta.com/oauth2/default"));
http.cors();
}
}
AccountController.java
@RestController
@CrossOrigin(origins = {"http://localhost:4200"})
@RequestMapping("/account")
public class AccountController {
@Autowired
private SecurityContextAccessor securityContextAccessor;
@Autowired
private OrderService orderService;
@PostMapping(value = "/orders")
public @ResponseBody ResponseEntity<String> getOrders() {
try{
return new ResponseEntity<>(orderService.getOrders(securityContextAccessor.getJwtToken()), HttpStatus.OK);
}catch(AccountException ae){
return new ResponseEntity<>("", HttpStatus.FORBIDDEN);
}
}
}
更新
当我将控制器从 POST 更改为 GET 时,问题得到解决,想知道为什么 POST 请求失败?
根据 当前建议,您的 Angular 应用程序不应配置为 OAuth2“公共”客户端(从授权服务器获取令牌)。相反,您应该使用 BFF 模式,并在服务器上运行 OAuth2“机密”客户端,并在 Angular 和 BFF 之间使用会话和 CSRF 保护(其配置错误可能是导致当前问题的原因)授权请求。我为此编写了一个教程,它适用于任何 OIDC 提供商(包括 Okta)。
请注意,我第一个链接的帖子是由 Spring Security 团队在 2021 年中编写的,自那时起教程应该已修复,但是......
此外,
WebSecurityConfigurerAdapter
来自旧版本的 Spring Security,只有非常不推荐使用的版本不支持定义 Web 安全性的新方法(公开 SecurityFilterChain
beans)。你应该切换到那个。