📜  Angular + Spring登录和注销示例(1)

📅  最后修改于: 2023-12-03 15:29:23.038000             🧑  作者: Mango

Angular + Spring登录和注销示例

在Web应用程序中,登录和注销是基础功能之一。通过使用Angular和Spring,可以很方便地实现这两个功能。本文将展示如何使用Angular和Spring实现登录和注销功能。

技术栈
  • Angular
  • Spring Boot
  • Spring Security
  • JWT
实现过程
后端实现

首先,在Spring Boot应用程序中集成Spring Security进行用户身份验证。使用JWT(JSON Web Token)生成令牌,该令牌用于在Web应用程序中进行身份验证。

  1. 引入相关依赖

在Maven的pom.xml配置文件中添加以下依赖:

<!-- Spring Security -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- JWT -->
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.1</version>
</dependency>
  1. 创建User model

创建User model,用于在数据库中管理用户和密码。这里使用H2数据库,并提供了一个测试用户名和密码。

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String username;

    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    // getters and setters
}
  1. 创建User repository

创建User repository,用于与数据库进行交互。在这里,我们只需要定义一个方法来查找用户。

public interface UserRepository extends JpaRepository<User, Long> {

    User findByUsername(String username);
}
  1. 创建Security configuration

创建Security configuration,用于配置Spring Security。配置过程中,我们指定了用户名和密码,并使用JWT生成令牌。

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final UserDetailsService userDetailsService;

    public SecurityConfiguration(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/api/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilter(new JwtAuthenticationFilter(authenticationManager()))
                .addFilter(new JwtAuthorizationFilter(authenticationManager()))
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}
  1. 创建JwtAuthenticationFilter

创建JwtAuthenticationFilter,当用户提交用户名和密码时,检查它们是否正确,并生成JWT令牌。

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
            return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        String token = Jwts.builder()
                .setSubject(((User) authResult.getPrincipal()).getUsername())
                .setExpiration(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SecurityConstants.SECRET.getBytes())
                .compact();
        response.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
    }
}
  1. 创建JwtAuthorizationFilter

创建JwtAuthorizationFilter,它将从Http请求头中获取JWT令牌并对其进行验证。

public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

    public JwtAuthorizationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String header = request.getHeader(SecurityConstants.HEADER_STRING);

        if (header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX)) {
            chain.doFilter(request, response);
            return;
        }
        Authentication authentication = getAuthentication(request);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(request, response);
    }

    private Authentication getAuthentication(HttpServletRequest request) {
        String token = request.getHeader(SecurityConstants.HEADER_STRING);
        if (token != null) {
            String user = Jwts.parser()
                    .setSigningKey(SecurityConstants.SECRET.getBytes())
                    .parseClaimsJws(token.replace(SecurityConstants.TOKEN_PREFIX, ""))
                    .getBody()
                    .getSubject();

            if (user != null) {
                return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
            }
            return null;
        }
        return null;
    }
}
  1. 创建SecurityConstants

创建SecurityConstants,用来存储常量并提供JWT密钥。

public class SecurityConstants {

    public static final String SECRET = "mysecret";
    public static final long EXPIRATION_TIME = 864_000_000;
    public static final String TOKEN_PREFIX = "Bearer ";
    public static final String HEADER_STRING = "Authorization";
}
前端实现

现在,我们来看看如何在Angular应用程序中实现登录和注销功能。

  1. 创建login组件

使用Angular CLI创建一个名为login的组件。

ng generate component login
  1. 实现login组件

在login.component.ts文件中,实现submit方法,该方法将处理提交的数据并调用相应的服务来验证访问权限。

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { UserService } from '../services/user.service';

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

  username: string;
  password: string;

  constructor(
    private userService: UserService,
    private router: Router
  ) { }

  ngOnInit(): void {
  }

  submit() {
    this.userService.login(this.username, this.password)
      .subscribe(
        result => {
          localStorage.setItem('token', result.token);
          this.router.navigateByUrl('/home');
        },
        error => console.log(error)
      );
  }
}

在login.component.html文件中,使用Angular表单处理用户输入。

<form (ngSubmit)="submit()" #loginForm="ngForm">
  <div class="form-group">
    <label for="username">Username:</label>
    <input type="text"
           class="form-control"
           id="username"
           name="username"
           [(ngModel)]="username"
           required>
  </div>
  <div class="form-group">
    <label for="password">Password:</label>
    <input type="password"
           class="form-control"
           id="password"
           name="password"
           [(ngModel)]="password"
           required>
  </div>
  <button type="submit" class="btn btn-primary">Submit</button>
</form>
  1. 添加AuthGuard

创建AuthGuard并添加到应用程序路由中,以验证用户是否有权访问受保护的页面。如果用户未经身份验证,则将重定向到登录页面。

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { UserService } from '../services/user.service';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {

  constructor(
    private userService: UserService,
    private router: Router
  ) { }

  canActivate(): boolean {

    if (!this.userService.isLoggedIn()) {
      this.router.navigateByUrl('/login');
      return false;
    }
    return true;
  }
}
  1. 创建userService

创建userService,它将与后端进行通信,获取JWT令牌并将其存储在localStorage中。

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { JwtResponse } from '../model/JwtResponse';

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

  private loginUrl = '/api/login';

  constructor(private http: HttpClient) {
  }

  login(username: string, password: string): Observable<JwtResponse> {
    return this.http.post<JwtResponse>(this.loginUrl, {username, password});
  }

  logout() {
    localStorage.removeItem('token');
  }

  isLoggedIn() {
    return localStorage.getItem('token') !== null;
  }
}
  1. 创建home页面

创建home页面并添加注销按钮。

在home.component.ts文件中,实现logout方法,该方法将调用userService的logout方法来删除JWT令牌。

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { UserService } from '../services/user.service';

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

  constructor(
    private userService: UserService,
    private router: Router
  ) { }

  ngOnInit(): void {
  }

  logout() {
    this.userService.logout();
    this.router.navigateByUrl('/login');
  }
}

在home.component.html文件中,创建一个注销按钮。

<button class="btn btn-outline-danger" (click)="logout()">Logout</button>
运行应用
  1. 启动后端

在Eclipse或其他Java IDE中运行Spring Boot程序。

  1. 启动前端

使用Angular CLI在命令行中启动Angular应用程序。

ng serve
  1. 登录

使用任意用户名和密码登录。

  1. 访问受保护的页面

在home页面中,访问受保护的页面并单击注销按钮。

总结

本文介绍了如何使用Angular和Spring Boot实现登录和注销功能。通过使用JWT生成令牌,在前端和后端之间进行身份验证。JWT是一种轻量级的身份验证方法,生成的令牌可实现终端到端的身份验证。