首页 > 解决方案 > 使用spring security angular和rest API重新加载页面给我错误401

问题描述

我使用 Spring Boot、Spring Security 和 Angular 客户端进行了相对简单的设置。所以我使用rest api在客户端上显示数据库,就像数据库网站一样

所以现在登录正在工作,当我登录时,我可以一切都没有问题。但是当我重新加载页面时,我失去了对其余 api 的授权,我无法再收到信息,并出现错误 401。

这是在登录过程之后

General
Request URL: http://localhost:8080/mindnessBdd/api/v1/films
Request Method: GET
Status Code: 200 
Remote Address: [::1]:8080
Referrer Policy: no-referrer-when-downgrade

Request header
GET /mindnessBdd/api/v1/films HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Accept: application/json, text/plain, */*
Sec-Fetch-Dest: empty
Authorization: Basic bWluZG5lc3M6QFJhcGhhNzY2MjAh
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
Content-Type: application/json
Origin: http://localhost:4200
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Referer: http://localhost:4200/films
Accept-Encoding: gzip, deflate, br
Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7

Responce header
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:4200
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/json
Date: Tue, 10 Mar 2020 09:32:31 GMT
Expires: 0
Pragma: no-cache
Transfer-Encoding: chunked
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

并在重新加载后

General
Request URL: http://localhost:8080/mindnessBdd/api/v1/films
Request Method: GET
Status Code: 401 
Remote Address: [::1]:8080
Referrer Policy: no-referrer-when-downgrade

Response Headers
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:4200
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Length: 0
Date: Tue, 10 Mar 2020 09:37:25 GMT
Expires: 0
Pragma: no-cache
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
WWW-Authenticate: Basic realm="Realm"
WWW-Authenticate: Basic realm="Realm"
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

Request Header
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7
Authorization: Basic dW5kZWZpbmVkOnVuZGVmaW5lZA==
Connection: keep-alive
Content-Type: application/json
Host: localhost:8080
Origin: http://localhost:4200
Referer: http://localhost:4200/films
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36

不知道它是否有帮助,但这是我的身份验证配置

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .httpBasic()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().formLogin().disable();;

    }
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        final CorsConfiguration configuration = new CorsConfiguration();

        configuration.setAllowedOrigins(ImmutableList.of("http://localhost:4200"));
        configuration.setAllowedMethods(ImmutableList.of("*"));
        configuration.setAllowCredentials(true);
        configuration.setMaxAge((long) 3600);
        configuration.setAllowedHeaders(ImmutableList.of("*"));
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

}

我认为我对 Spring Security 缺乏了解是错误的,任何人都可以看到我做错了什么吗?

提前致谢 !

** 编辑登录组件:

Skip to content
Search or jump to…

Pull requests
Issues
Marketplace
Explore

@mindness 
mindness
/
WebAppFront
1
00
 Code Issues 0 Pull requests 0 Actions Projects 0 Wiki Security Insights Settings
WebAppFront/src/app/login/login.component.ts / 
 raphael lefrancois login / auth / logout
c3fe322 4 days ago
Executable File  39 lines (33 sloc)  1 KB

Code navigation is available!
Navigate your code with ease. Click on function and method calls to jump to their definitions or references in the same repository. Learn more

You're using code navigation to jump to definitions or references.
Learn more or give us feedback
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { AuthenticationService } from './auth.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

  username: string;
  password: string;
  errorMessage = 'Invalid Credentials';
  successMessage: string;
  invalidLogin = false;
  loginSuccess = false;

  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private authenticationService: AuthenticationService) {   }

  ngOnInit() {
  }

  handleLogin() {
    this.authenticationService.authenticationService(this.username, this.password).subscribe((result) => {
      this.invalidLogin = false;
      this.loginSuccess = true;
      this.successMessage = 'Login Successful.';
      this.router.navigate(['/films']);
    }, () => {
      this.invalidLogin = true;
      this.loginSuccess = false;
    });
  }

}


认证服务

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class AuthenticationService {

  // BASE_PATH: 'http://localhost:8080'
  USER_NAME_SESSION_ATTRIBUTE_NAME = 'authenticatedUser'

  public username: string;
  public password: string;

  constructor(private http: HttpClient) {

  }

  authenticationService(username: string, password: string) {
    return this.http.get(`http://localhost:8080/mindnessBdd/api/v1/basicauth`,
      { headers: { authorization: this.createBasicAuthToken(username, password) } }).pipe(map((res) => {
      this.username = username;
      this.password = password;
      this.registerSuccessfulLogin(username, password);
    }));
  }

  createBasicAuthToken(username: string, password: string) {
    return 'Basic ' + window.btoa(username + ':' + password);
  }

  registerSuccessfulLogin(username, password) {
    sessionStorage.setItem(this.USER_NAME_SESSION_ATTRIBUTE_NAME, username);
  }

  logout() {
    sessionStorage.removeItem(this.USER_NAME_SESSION_ATTRIBUTE_NAME);
    this.username = null;
    this.password = null;
  }

  isUserLoggedIn() {
    const user = sessionStorage.getItem(this.USER_NAME_SESSION_ATTRIBUTE_NAME)
    if (user === null) { return false; }
    return true;
  }

  getLoggedInUserName() {
    const user = sessionStorage.getItem(this.USER_NAME_SESSION_ATTRIBUTE_NAME)
    if (user === null) { return ''; }
    return user;
  }
}

http拦截器

import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { AuthenticationService } from './auth.service';

@Injectable()
export class HttpInterceptorService implements HttpInterceptor {

  constructor(private authenticationService: AuthenticationService) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (this.authenticationService.isUserLoggedIn() && req.url.indexOf('basicauth') === -1) {
      const authReq = req.clone({
        headers: new HttpHeaders({
          'Content-Type': 'application/json',
          Authorization: `Basic ${window.btoa(this.authenticationService.username + ':' + this.authenticationService.password)}`
        })
      });
      return next.handle(authReq);
    } else {
      return next.handle(req);
    }
  }
}

标签: angularspringspring-bootrestspring-security

解决方案


这是一个非常有趣的问题。

如果您仔细检查两个请求标头(登录后的标头和重新加载后的标头),您会找到原因。

登录后的授权由 Base64 编码为:

Authorization: Basic bWluZG5lc3M6QFJhcGhhNzY2MjAh

重新加载后的一个是:

Authorization: Basic dW5kZWZpbmVkOnVuZGVmaW5lZA==

现在,如果您将第二次授权解码为纯文本,则为:

undefined:undefined

这就是你得到 401 的原因,因为 undefined undefined 不是有效的用户和密码。

而且我猜你从你的javascript变量中获得了用户名和密码,当你重新加载你的页面时它是未定义的......


更新:阅读您的前端代码后,您似乎将用户名保存到了会话存储中,并且当您重新加载页面时,它假定用户已登录,因为它在会话存储中找到了用户名。因此它尝试再次发送令牌,但是未设置身份验证服务中的用户名和密码。您可以将用户名和令牌保存到会话存储 (cookie) 中。并且当重新加载时,它可以从存储中获取令牌,而不是。为了安全起见,设置 HttpOnly 标志。


推荐阅读