📅  最后修改于: 2023-12-03 15:29:23.038000             🧑  作者: Mango
在Web应用程序中,登录和注销是基础功能之一。通过使用Angular和Spring,可以很方便地实现这两个功能。本文将展示如何使用Angular和Spring实现登录和注销功能。
首先,在Spring Boot应用程序中集成Spring Security进行用户身份验证。使用JWT(JSON Web Token)生成令牌,该令牌用于在Web应用程序中进行身份验证。
在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>
创建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
}
创建User repository,用于与数据库进行交互。在这里,我们只需要定义一个方法来查找用户。
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
创建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);
}
}
创建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);
}
}
创建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;
}
}
创建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应用程序中实现登录和注销功能。
使用Angular CLI创建一个名为login的组件。
ng generate component 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>
创建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;
}
}
创建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;
}
}
创建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>
在Eclipse或其他Java IDE中运行Spring Boot程序。
使用Angular CLI在命令行中启动Angular应用程序。
ng serve
使用任意用户名和密码登录。
在home页面中,访问受保护的页面并单击注销按钮。
本文介绍了如何使用Angular和Spring Boot实现登录和注销功能。通过使用JWT生成令牌,在前端和后端之间进行身份验证。JWT是一种轻量级的身份验证方法,生成的令牌可实现终端到端的身份验证。