【问题标题】:401 unauthorized error for Okta+Springboot+angularOkta+Springboot+angular 的 401 未授权错误
【发布时间】:2021-05-24 05:42:11
【问题描述】:

我正在开发一个带有 Okta 身份验证的 springboot+angular 应用程序,但我收到 401 Unauthorized 错误。以下是我在前端的代码:

app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { OktaCallbackComponent } from '@okta/okta-angular';
import { OktaAuthGuard } from './app.guard';
import { UserDetailsComponent } from './components/user-details/user-details.component';

const routes: Routes = [
 { path: 'home', canActivate: [OktaAuthGuard], component: UserDetailsComponent },
 { path: 'def', component: OktaCallbackComponent },
 { path: '**', redirectTo: '', pathMatch: 'full' },
];

@NgModule({
 imports: [RouterModule.forRoot(routes)],
 exports: [RouterModule]
})
export class AppRoutingModule { }

app.component.ts

import { Component } from '@angular/core';
import { OktaAuthService } from '@okta/okta-angular';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'users';
isAuthenticated: boolean;
constructor(public oktaAuth: OktaAuthService) {
    // subscribe to authentication state changes
    this.oktaAuth.$authenticationState.subscribe(
    (isAuthenticated: boolean)  => this.isAuthenticated = isAuthenticated
    );
}
async ngOnInit() {
    // get authentication state for immediate use
    this.isAuthenticated = await this.oktaAuth.isAuthenticated();
}
async login() {
    await this.oktaAuth.signInWithRedirect({
    originalUri: '/users'
    });
}
async logout() {
    await this.oktaAuth.signOut();
}
}

app.guard.ts

import { OktaAuthService } from '@okta/okta-angular';
import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

@Injectable({
providedIn: 'root'
})
export class OktaAuthGuard implements CanActivate {
oktaAuth;
authenticated;
constructor(private okta: OktaAuthService, private router: Router) {
    this.oktaAuth = okta;
}

async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    this.authenticated = await this.okta.isAuthenticated();
    console.log('can activate?', this.authenticated);
    if (this.authenticated) { return true; }
    // Redirect to login flow.
    this.okta.signInWithRedirect();
    return false;
}
}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
//import { OktaAuthModule, OktaCallbackComponent } from '@okta/okta-angular';
import {OktaAuthModule, OKTA_CONFIG} from '@okta/okta-angular';
import { UserDetailsComponent } from './components/user-details/user-details.component';
import { ReactiveFormsModule } from '@angular/forms';
import { OktaAuthGuard } from './app.guard';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { XSRFTokenInterceptor } from './xsrf-token-interceptor';
import { AuthInterceptor } from './auth.interceptor';

const oktaConfig = {
issuer: 'https://dev-xxxxxxxx.okta.com/oauth2/default',
redirectUri: window.location.origin + '/users',
clientId: 'xxxxxxxxxxxxxxx',
pkce: true
};

@NgModule({
declarations: [
    AppComponent,
    UserDetailsComponent
],
imports: [
    BrowserModule,
    OktaAuthModule,
    AppRoutingModule,
    HttpClientModule
],
providers: [
    OktaAuthGuard,
    {provide: HTTP_INTERCEPTORS, useClass : XSRFTokenInterceptor, multi: true},
    { provide: OKTA_CONFIG, useValue: oktaConfig },
    {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true},],
bootstrap: [AppComponent]
})
export class AppModule { }

auth.interceptor.ts

import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable, from } from 'rxjs';
import { OktaAuthService } from '@okta/okta-angular';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

constructor(private oktaAuth: OktaAuthService) {
}

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return from(this.handleAccess(request, next));
}

private async handleAccess(request: HttpRequest<any>, next: HttpHandler): Promise<HttpEvent<any>> {

    const accessToken = await this.oktaAuth.getAccessToken();

    if (accessToken) {
    console.log('token: ' + accessToken);
    request = request.clone({
        setHeaders: {
        Authorization: 'Bearer ' + accessToken
        }
    });
    }
    return next.handle(request).toPromise();
}
}

user.service.ts

 import { HttpClient } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 import { Observable } from 'rxjs';
 import { environment } from 'src/environments/environment';
 import { map } from 'rxjs/operators';
 import { HandleError, HttpErrorHandler } from './http-error-handler.service';
 import { catchError } from 'rxjs/operators';

@Injectable({
providedIn: 'root'
})
export class UserService {
private handleError: HandleError;
contactsUrl = environment.contactAPI;
constructor(private http: HttpClient,
    httpErrorHandler: HttpErrorHandler) {
    this.handleError = httpErrorHandler.createHandleError('HeroesService');
}

getUsers() {
 return this.http.get<any>(this.contactsUrl)
    .pipe(map(info => {
        console.log("info: "+info);            
        return info;
    }));

}}

xsrf-token-interceptor.ts

 import { Injectable } from '@angular/core';
 import {HttpEvent, HttpRequest, HttpHandler,HttpInterceptor, HttpErrorResponse, 
  HttpXsrfTokenExtractor} from '@angular/common/http';
 import { Observable } from 'rxjs';

@Injectable()
export class XSRFTokenInterceptor implements HttpInterceptor {

constructor(private tokenExtractor: HttpXsrfTokenExtractor) {}

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
     return next.handle(req);
   }
}

在后端:

SecurityDevConfiguartion.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpMethod;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.cors.CorsConfiguration;
import java.util.Arrays;


@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)

public class SecurityDevConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
    System.out.println("configure");
    http.cors().and().csrf().disable();
  try {
        http.antMatcher("/**")
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/").permitAll()
                .anyRequest().authenticated();
    }catch(Exception e){
        e.printStackTrace();
    }
    http.oauth2ResourceServer();
}

@Bean
CorsConfiguration corsConfiguration() {
    CorsConfiguration corsConfiguration  = new CorsConfiguration();
    corsConfiguration.setAllowCredentials(true);
    corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
    corsConfiguration.setAllowedHeaders(Arrays.asList("Origin", "Access-Control, Allow-Origin", "Content-Type", "Accept", "Authorization", "Origin, Accept", "X-Requested-With", "Access-Control-Request-Method", "Access-Control-Request-Header" )); 
    corsConfiguration.setExposedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Authorization", "Access-Control-Request-Allow-Origin", "Access-Control-Allow-Credentials"));
    corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
    
    return corsConfiguration ;
}
}

UsersController.java

import com.example.users.dao.usersDAO;
import com.example.users.model.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

@RestController
@CrossOrigin(origins = "http://localhost:4200")
public class UsersController {

@Autowired
usersDAO usersDAO;

@RequestMapping(value = "/getUsers", method = RequestMethod.GET)
@ResponseBody
public List getUsers(@AuthenticationPrincipal Principal userInfo) throws Exception {
    System.out.println("userInfo: "+userInfo);
    List<Users> users = usersDAO.findAll();
    List<String> l = new ArrayList();
    for(Iterator i=Users.iterator();i.hasNext();){
        Users u = (Users)(i.next());
        l.add(u.getName());
    }
    return l;
}
}

application.properties 提到了客户端 ID 和颁发者。在 Okta 安全性的可信来源中提到了“http//localhost:4200”。

我错过了什么/做错了什么?任何帮助表示赞赏。

【问题讨论】:

    标签: angular spring-boot okta-api


    【解决方案1】:

    您的 Okta 租户是否启用了 API 访问管理功能? 通过此授权服务器创建的访问令牌可以在本地验证,因为它们的签名密钥在 /keys 端点下返回。

    此功能对开发人员免费,但在生产环境中付费。如果您想在生产租户上启用此功能。

    【讨论】:

    • 如何检查该功能是否启用?我已将授权服务器设置为我的开发者帐户的默认设置。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-14
    相关资源
    最近更新 更多