掘金 后端 ( ) • 2024-04-23 14:55

简介:

我们将探讨如何设置身份验证和API网关。我们将深入了解Auth Service和API Gateway的配置细节和基本代码片段。

创建Auth服务:为微服务添加身份验证

Auth服务是我们系统中管理用户身份验证的重要部分。我们将指导您完成设置此基本服务所需的代码和配置。

应用程序配置:
下面是Auth Service的application.yml文件:

spring:
  application:
    name: auth-service
  data:
    mongodb:
      uri: mongodb+srv://userid:[email protected]/?retryWrites=true&w=majority
      database: ams
server:
  port: 9003
eureka:
  instance:
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

因为我们使用Mongo-DB来存储数据。

1.存储库和模型类

AccountRepo.java
AccountRepo是Spring Data MongoDB存储库接口。它扩展了MongoRepository,并允许您在MongoDB中对Account集合执行各种操作。在本例中,它包含一个自定义方法findByUserName,用于通过用户名查找帐户。

@Repository
public interface AccountRepo extends MongoRepository<Account, Integer> {
    Account findByUserName(String username);
}

模型类别:

//Create Account.java Model class
@Data
@Document(collection = "accounts")
public class Account {
    @Id
    private Integer id;
    private String firstName;
    private String lastName;
    private String userName;
    private String createdAt;
    private String password;
    @CreatedDate
    private LocalDate createdDate;
}
//Create LoginRequest.java Model class
@Data
public class LoginRequest {
    private String username;
    private String password;
}
//Create LoginResponse.java Model class
@Data
public class LoginResponse {
    private String token;
    private String userName;
}

2. Auth Controller(AuthController.java):

AuthController负责处理用户注册、登录和令牌验证。

package com.auth.controller;

imports ****

@RestController
@RequestMapping("/auth")
public class AuthController {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private AuthService authServicee;



    @PostMapping("/register")
    public Account createAmsAccount(@RequestBody Account account) throws Exception {
        return authServicee.saveAccount(account);
    }


    @PostMapping("/login")
    public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest loginRequest) {
        Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
        String token ;
        if (authenticate.isAuthenticated()) {
            token = authServicee.generateToken(loginRequest.getUsername());
        } else {
            throw new RuntimeException("invalid access");
        }
        LoginResponse response = LoginResponse.builder()
                .token(token)
                .userName(loginRequest.getUsername()).build();
        return ResponseEntity.ok(response);
    }

    @GetMapping("/validate")
    public String validateToken(@RequestParam("token") String token) {
        try {
            authServicee.validateToken(token);
        } catch (BadCredentialsException e) {
            throw new BadCredentialsException(" Invalid Token ");
        }
        return "Token is valid";
    }


}

在这段代码中,我们定义了用于注册、登录和令牌验证的端点。使用Spring Security和JWT令牌执行用户身份验证。

3.安全配置(authConfig.java):

authConfig类包含与安全相关的配置。它定义了自定义的UserDetailsService、安全过滤器链、密码编码和身份验证提供程序。

@Configuration
@EnableWebSecurity
public class authConfig {

    @Bean
    public UserDetailsService userDetailsService() {
        return new CustomUserDetailsService();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.csrf().disable()
                .authorizeHttpRequests()
                .requestMatchers("/auth/register", "/auth/login", "/auth/validate").permitAll()
                .and()
                .build();
    }

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

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService());
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }
}

在这个配置类中,我们定义了安全设置,包括UserDetailsService、身份验证过滤器链、密码编码和身份验证提供程序。

4.自定义用户详细信息服务(CustomUserDetailsService.java):

CustomUserDetailsService实现了UserDetailsService接口,为身份验证提供了用户详细信息。

public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private AccountRepo accountRepo;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        try {
            Account account = accountRepo.findByUserName(username);
            String userName = account.getUserName();
            String password = account.getPassword();
            return new User(userName, password, new ArrayList<>());
        } catch (Exception e) {
            throw new UsernameNotFoundException(e.toString());
        }
    }
}

这个类从数据库中检索用户详细信息,使Spring Security能够对用户进行身份验证。

5.认证服务(AuthService.java):

AuthService类管理用户帐户操作,包括保存帐户、生成令牌和验证令牌。

@Service
public class AuthService {

    @Autowired
    private AccountRepo accountRepo;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private JwtService jwtService;

    public Account saveAccount(Account account) throws Exception {
        account.setPassword(passwordEncoder.encode(account.getPassword()));
        return accountRepo.save(account);
    }

    public String generateToken(String username) {
        return jwtService.generateToken(username);
    }

    public void validateToken(String token) {
        jwtService.validateToken(token);
    }
}

这个服务类使用JWT处理用户帐户操作、密码加密和令牌管理。

6. JWT服务(JwtService.java):

JwtService类负责JWT令牌的生成和验证。

@Service
public class JwtService {

    public static final String SECRET = "Random generate secret key";

    public void validateToken(final String token) {
        Jwts.parserBuilder().setSigningKey(getSignKey()).build().parseClaimsJws(token);
    }

    public String generateToken(String userName) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userName);
    }

    private String createToken(Map<String, Object> claims, String userName) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(userName)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 30))
                .signWith(getSignKey(), SignatureAlgorithm.HS256).compact();
    }

    private Key getSignKey() {
        byte[] keyBytes = Decoders.BASE64.decode(SECRET);
        return Keys.hmacShaKeyFor(keyBytes);
    }
}

JwtService类负责生成和验证JWT令牌,增强Auth Service的安全性。

创建API网关:管理微服务通信。

API网关充当所有请求的入口点,并管理将这些请求路由到适当的微服务。以下是如何配置它:

应用程序配置:
API网关的application.yml文件:

spring:
  application:
    name: api-gateway
  main:
    allow-bean-definition-overriding: true
  cloud:
    gateway:
      routes:
        - id: account-service
          uri: lb://account-service
          predicates:
            - Path=/account/**
          filters:
            - AuthenticationFilter

        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user/**
          filters:
            - AuthenticationFilter

        - id: auth-service
          uri: lb://auth-service
          predicates:
            - Path=/auth/**
eureka:
  instance:
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka
server:
  port: 9000

ApigatewayApplication.java

package com.apigateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
public class ApigatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(ApigatewayApplication.class, args);
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

这是API网关的主类。它被注释为@SpringBootApplication以指示它是Spring靴子应用程序。@EnableDiscoveryClient注释支持使用尤里卡进行服务发现。@LoadBalanced注释配置了一个RestTemplate,它可以在服务实例之间进行负载平衡。

AuthenticationFilter.java

package com.apigateway.filter;

import com.apigateway.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ExceptionHandler;

@Component
public class AuthenticationFilter extends AbstractGatewayFilterFactory<AuthenticationFilter.Config> {

    @Autowired
    private RouteValidator validator;

    @Autowired
    private JwtUtil jwtUtil;

    public AuthenticationFilter() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return ((exchange, chain) -> {
            if (validator.isSecured.test(exchange.getRequest())) {
                if (!exchange.getRequest().getHeaders().containsKey(HttpHeaders.AUTHORIZATION)) {
                    throw new RuntimeException("Missing authorization header");
                }

                String authHeader = exchange.getRequest().getHeaders().get(HttpHeaders.AUTHORIZATION).get(0);
                if (authHeader != null && authHeader.startsWith("Bearer ")) {
                    authHeader = authHeader.substring(7);
                }
                try {
                    jwtUtil.validateToken(authHeader);
                } catch (Exception e) {
                    System.out.println("Invalid access...!");
                    throw new RuntimeException("Unauthorized access to the application");
                }
            }
            return chain.filter(exchange);
        });
    }

    public static class Config {

    }
}

AuthenticationFilter是API网关的自定义过滤器。它检查传入请求中是否存在Authorization头。如果头部存在并且包含有效的JWT令牌,则允许请求通过。否则,它会抛出一个未经授权的访问RuntimeException
此过滤器确保只有具有有效JWT令牌的经过身份验证的请求才能访问安全端点。它使用JwtUtil来验证JWT令牌。

RouteValidator.java

package com.apigateway.filter;

import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.function.Predicate;

@Component
public class RouteValidator {

    public static final List<String> openApiEndpoints = List.of(
            "/auth/register",
            "/auth/login",
            "/eureka"
    );

    public Predicate<ServerHttpRequest> isSecured =
            request -> openApiEndpoints
                    .stream()
                    .noneMatch(uri -> request.getURI().getPath().contains(uri));
}

这些类和配置文件有助于实现API网关,该网关将请求路由到适当的服务,并根据指定的路由强制执行安全检查。