从零开始搭建高负载java架构(05)——gateway网关节点(权限验证篇)

目录

1. 基本原理

 2. 具体实现

2.1 通过mysql加载用户信息的实现

 2.2 实现基本的Security逻辑的配置类实现

2.3 实现基本的访问路径资源权限

2.4 登录验证中常用的回调函数自定义

 2.4.1 自定义登录成功回调ServerAuthenticationSuccessHandler

2.4.2 自定义登录失败回调ServerAuthenticationFailureHandler

2.4.3 自定义因未登录而访问未授权路径的回调ServerAuthenticationEntryPoint

 2.4.4 自定义访问未授权资源路径的回调ServerAccessDeniedHandler

2.4.5 自定义成功退出登录的回调ServerLogoutSuccessHandler

2.4.6 最终配置类引用上述自定义回调的修改


1. 基本原理

spring gateway采用的是webflux的反应式实现,因此对应的sring security也需要用webflux的处理方式,个人整理的WebFlux应用的认证过程如下:

  1. 用户通过form表单输入用户名和密码,通过POST请求登录认证
  2. 后台通过ServerFormLoginAuthenticationConverter类的convert接口,把获取到的用户名和密码封装成一个UsernamePasswordAuthenticationToken对象(Authentication的子类),然后把它传递给 ReactiveAuthenticationManager进行认证。
  3. ReactiveAuthenticationManager的认证逻辑接口authenticate
    (1) 如果认证失败则返回null,程序会自动跳到第1 步让用户重新输入用户名和密码
    (2) 如果认证成功则返回UsernamePasswordAuthenticationToken对象(authenticated字段被设置为true),并进行下面的步骤
  4. 将返回的UsernamePasswordAuthenticationToken对象通过类ServerSecurityContextRepository的save接口,把UsernamePasswordAuthenticationToken对象保存到SecurityContext对象中
  5. 然后默认会将用户重定向到之前访问的页面。
  6. 用户登录认证成功后再次访问之前受保护的资源时,就会调用ReactiveAuthorizationManager类的check接口对用户访问的路径进行权限鉴定,如不存在对应路径的访问权限,则默认会返回 403 错误码(如果要特殊处理,可以实现ServerAccessDeniedHandler类的handle接口,返回特定的信息)。

【注意】另外,如果要通过数据库或redis查找用户信息,可以重载实现ReactiveUserDetailsService的接口findByUsername,从其他数据源里查出user数据转成UserDetails对象,如果除了用户名和密码,还有其他额外的用户信息需要保存,可以重置UserDetails类,添加额外的信息

基本逻辑类都在包: spring-security-core-6.0.3.jar中

  1. AuthenticationWebFilter登录认证过滤器的filter接口
    包路径:org.springframework.security.web.server.authentication
  2. ServerFormLoginAuthenticationConverter 登录认证:
    包路径:org.springframework.security.web.server.authentication
    调用包:org.springframework.security.web.server的同名类:
    ServerFormLoginAuthenticationConverter的apply接口
    取Form转成Map的username和password
  3. ServerHttpBasicAuthenticationConverter类
    包路径:org.springframework.security.web.server.authentication
    http头字段:Authorization
    格式:Basic base64(用户名:密码)
  4. 默认的观察者模式ReactiveAuthenticationManager类:
    ObservationReactiveAuthenticationManager
    包路径:org.springframework.security.authentication
  5. 默认的ReactiveAuthenticationManager:
    UserDetailsRepositoryReactiveAuthenticationManager
    (主要验证逻辑在父类:AbstractUserDetailsReactiveAuthenticationManager)
    验证通过在createUsernamePasswordAuthenticationToken接口中设置登录验证通过标识authenticated为true
    包路径:org.springframework.security.authentication
  6. 默认的ServerSecurityContextRepository:
    WebSessionServerSecurityContextRepository
  7. 默认的SecurityContext:
    SecurityContextImpl
    包路径:org.springframework.security.core.context
  8. 默认的ReactiveAuthorizationManager:
    AuthenticatedReactiveAuthorizationManager<AuthorizationContext>
    包路径:org.springframework.security.authorization

 2. 具体实现

2.1 通过mysql加载用户信息的实现

基本的重载方法,为了支持从数据库查找用户登录信息,实现接口类:UserDetails

package com.cloudservice.gateway_service.security;
import java.util.Collection;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class GatewayUserDetails implements UserDetails {
    private Long id;            // 用户id
    private String password;    // 密码
    private String username;    // 用户名
    private boolean enabled;    // 帐户是否可用
    private Set<GatewayUserGrantedAuthority> authorities;  // 权限信息
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }
    public void setAuthorities(Set<GatewayUserGrantedAuthority> authorities) {
        this.authorities = authorities;
    }
    @Override
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    @Override
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public boolean isAccountNonExpired() {
        // TODO 帐号是到到期
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        // TODO 帐号是否锁定
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        // TODO 密码是否到期
        return true;
    }
    @Override
    public boolean isEnabled() {
        return this.enabled;
    }
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }
}

定义用户表的领域类:Users

package com.cloudservice.gateway_service.security;
import java.util.HashSet;
import java.util.Set;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Users {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @NotNull
    @Size(min=5, message="Username must be at least 5 characters long")
    private String username;
    @NotNull
    private String password;
    private boolean enabled;
    public GatewayUserDetails to_user_details() {
        GatewayUserDetails userDetails = new GatewayUserDetails();
        userDetails.setId(id);
        userDetails.setUsername(username);
        userDetails.setPassword(password);
        userDetails.setEnabled(enabled);
        // 此处为了测试路径权限,写死order路径权限,实际中应该是从角色与路径的授权表groups_authorities里读取加载
        Set<GatewayUserGrantedAuthority> authorities = new HashSet<GatewayUserGrantedAuthority>();
        authorities.add(new GatewayUserGrantedAuthority("/order/"));
        userDetails.setAuthorities(authorities);
        return userDetails;
    }
    public static Users from_user_details(GatewayUserDetails userDetails) {
        return new Users(userDetails.getId()
                    , userDetails.getUsername()
                    , userDetails.getPassword()
                    , userDetails.isEnabled());
    }
}

简单通过使用JPA 的Repository类来根据用户名查数据库返回基本的用户信息:

package com.cloudservice.gateway_service.security;
import org.springframework.data.repository.CrudRepository;
public interface UsersRepository extends CrudRepository<Users, Long>{
    Iterable<Users> findByUsername(String username);
}

 网关的nacos配置里加上mysql的datasource配置:

spring:
  datasource:
    url: jdbc:mysql://192.168.10.111:32001/gateway?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true
    username: mysql用户名
    password: mysql密码
    driver-class-name: com.mysql.cj.jdbc.Driver

 自定义加载用户信息的类ReactiveUserDetailsService:

package com.cloudservice.gateway_service.security;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class GatewayReactiveUserDetailsService implements ReactiveUserDetailsService {
    @Autowired
    private UsersRepository usersRepository;
    @Override
    public Mono<UserDetails> findByUsername(String username) {
        // TODO 优先查找缓存再查找数据库
        // String Val1 = passwordEncoder.encode("123456");
        log.info("gateway find user: {} {}", username, DigestUtils.sha256Hex("123456"));
        List<UserDetails> userDetailsList = new ArrayList<UserDetails>();
        usersRepository.findByUsername(username).forEach(user -> {
            userDetailsList.add(user.to_user_details());
        });
        return Flux.fromIterable(userDetailsList).next();
    }
}

 2.2 实现基本的Security逻辑的配置类实现

package com.cloudservice.gateway_service.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
@Configuration
@EnableWebFluxSecurity
public class GatewaySecurityConfig {
  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }
  @Bean
  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
      .authorizeExchange()
        .pathMatchers("/favicon.*", "/login", "/logout").permitAll()
        .anyExchange().authenticated()
      .and().formLogin()
      .and().csrf().disable();
    return http.build();
  }
}

2.3 实现基本的访问路径资源权限

重载路径权限验证类 ReactiveAuthorizationManager<AuthorizationContext>,代码如下:

package com.cloudservice.gateway_service.security;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Component
public class GatewayReactiveAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext>{
    private AuthenticationTrustResolver authTrustResolver = new AuthenticationTrustResolverImpl();

    // 验证通过则返回:AuthorizationDecision(true)
    // 验证失败则返回:AuthorizationDecision(false)
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext context) {
		return authentication
                .filter(authentication_filter -> select_context(authentication_filter, context))
                .map(authentication_map -> authenticate(authentication_map, context))
				.defaultIfEmpty(new AuthorizationDecision(false));
    }
    private boolean select_context(Authentication authentication, AuthorizationContext context) {
        // String req_path = context.getExchange().getRequest().getURI().getPath();
        // log.info("check filter path: {}", req_path);
		return !this.authTrustResolver.isAnonymous(authentication);
	}
    private AuthorizationDecision authenticate(Authentication authentication, AuthorizationContext context) {
        if (authentication.isAuthenticated()) {
            // 判断context.getExchange().getRequest().getPath()是否在authentication_notanonymous.getAuthorities()集合中
            String req_path = context.getExchange().getRequest().getURI().getPath();
            if (authentication.getAuthorities().contains(new GatewayUserGrantedAuthority(req_path)) == false) {
                return new AuthorizationDecision(false);
            }
        }
        return new AuthorizationDecision(authentication.isAuthenticated());
    }
}

用户可访问路径的集合存放在authorities集合里,为了支持集合类的contains操作,必须重载GrantedAuthority类并实现equals和hashCode接口,具体实现代码如下:

package com.cloudservice.gateway_service.security;

import org.springframework.security.core.GrantedAuthority;

public class GatewayUserGrantedAuthority implements GrantedAuthority, java.lang.Comparable<Object> {
    String authority;
    public GatewayUserGrantedAuthority() {}
    public GatewayUserGrantedAuthority(String authority) {
        this.authority = authority;
    }
    @Override
    public String getAuthority() {
        return this.authority;
    }
    public void setAuthority(String authority) {
        this.authority = authority;
    }
    @Override
    public int compareTo(Object obj) {
        if (obj instanceof GatewayUserGrantedAuthority) {
            return this.authority.compareTo(((GatewayUserGrantedAuthority)obj).getAuthority());
        }
        return 1;
    }
    public boolean equals(Object obj) {
        if (!(obj instanceof GatewayUserGrantedAuthority)) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        return this.authority.equals(((GatewayUserGrantedAuthority)obj).getAuthority());
    }
    public int hashCode() {
        return this.authority.hashCode();
    }
}

修改配置类GatewaySecurityConfig的springSecurityFilterChain接口,引用路径鉴权类GatewayReactiveAuthorizationManager:

@Configuration
@EnableWebFluxSecurity
public class GatewaySecurityConfig {
  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }
  @Autowired
  private GatewayReactiveAuthorizationManager gatewayReactiveAuthorizationManager;
  @Bean
  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
      .authorizeExchange()
        .pathMatchers("/favicon.ico", "/login", "/logout").permitAll()
        .anyExchange().access(gatewayReactiveAuthorizationManager)
      .and().formLogin()
      .and().csrf().disable();
    return http.build();
  }
}

2.4 登录验证中常用的回调函数自定义

 2.4.1 自定义登录成功回调ServerAuthenticationSuccessHandler

通过自定义登录成功时的回调函数,可以自定义登录成功后的返回消息,vue3中可以直接返回json格式,方便判断后在vue3的js代码中实现提示后跳转逻辑,实现代码如下:

package com.cloudservice.gateway_service.security;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class CustomLoginSuccessHandler implements ServerAuthenticationSuccessHandler{
    @Override
    public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {
        ServerWebExchange exchange = webFilterExchange.getExchange();
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().add("Content-Type", "application/json; charset=UTF-8");

        log.info("user login success: {}", authentication.getName());

        Object principal = authentication.getPrincipal();
        ObjectMapper  objectMapper = new ObjectMapper();
        DataBuffer bodyDataBuffer = null;
        try {
            bodyDataBuffer = response.bufferFactory().wrap(objectMapper.writeValueAsBytes(principal));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return response.writeWith(Mono.just(bodyDataBuffer));
    }
}

2.4.2 自定义登录失败回调ServerAuthenticationFailureHandler

通过自定义登录失败回调,可以定制返回登录失败的消息,vue3里一般需要返回json格式数据,实现代码如下:

package com.cloudservice.gateway_service.security;
import java.util.HashMap;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class CustomLoginFailureHandler implements ServerAuthenticationFailureHandler {
    @Override
    public Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) {
        ServerHttpResponse response = webFilterExchange.getExchange().getResponse();
        response.setStatusCode(HttpStatus.FORBIDDEN);
        response.getHeaders().add("Content-Type", "application/json; charset=UTF-8");

        log.info("user login fail: {}", webFilterExchange.getExchange().getRequest().getPath());

        HashMap<String, String> map = new HashMap<String, String>();
        map.put("code", "-1001");
        if (exception instanceof LockedException) {
            map.put("message", "账户被锁定,请联系管理员!");
        } else if (exception instanceof BadCredentialsException) {
            map.put("message", "用户名或者密码输入错误,请重新输入!");
        } else {
            map.put("message", exception.getMessage());
        }
        DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONBytes(map));
        return response.writeWith(Mono.just(dataBuffer));
    }
}

2.4.3 自定义因未登录而访问未授权路径的回调ServerAuthenticationEntryPoint

通过自定义未登录而访问路径的错误回调 ,可以返回vue3需要的json格式,也可以强制跳转到登录页面,此处实现是强制跳转页面:

package com.cloudservice.gateway_service.security;
import java.net.URI;
import java.util.HashMap;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class CustomNoLoginHandler implements ServerAuthenticationEntryPoint {
    @Override
    public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException ex) {
        return Mono.fromRunnable(() -> {
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.FOUND);
            response.getHeaders().add("Content-Type", "application/json; charset=UTF-8");
            // 强制跳转到登录页面,在vue3中一般是返回json数据,并交由vue3来跳转
            response.getHeaders().setLocation(URI.create("/login"));
    
            log.info("url when no login: {}", exchange.getRequest().getPath());

            HashMap<String, Object> map = new HashMap<>();
            map.put("code", HttpStatus.FOUND.value());
            map.put("message", "暂未登录,请您先进行登录");
            DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONBytes(map));
            response.writeWith(Mono.just(dataBuffer));
        });
    }
}

【注意】加了此自定义回调时,不能正常访问security默认定义的login页面,需要自定义登录页面,本人简单的实现示例:

此示例的controller使用到thymeleaf的模板,因此需要在pom.xml中引用依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

创建一个用来接收GET方式的login请求的controller:

package com.cloudservice.gateway_service.security;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class CustomLoginControl {
    @GetMapping("/login")
    public String login() {
        return "login";
    }
}

 在src/main/resources/templates/目录下定义登录页面模板login.html

<html lang="en"><head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Please sign in</title>
</head>
<body>
<div class="container">
    <form method="post" action="/login">
        <h2>Please sign in</h2>
        <p>
            <label>Username</label>
            <input type="text" id="username" name="username" placeholder="Username" required="" autofocus="">
        </p>
        <p>
            <label>Password</label>
            <input type="password" id="password" name="password" placeholder="Password" required="">
        </p>
        <button type="submit">Sign in</button>
    </form>
</div>
</body>
</html>

 2.4.4 自定义访问未授权资源路径的回调ServerAccessDeniedHandler

当访问资源路径进行权限验证时,ReactiveAuthorizationManager验证不通过,则会回调此类的接口,可以自定义返回的数据格式,代码如下:

package com.cloudservice.gateway_service.security;
import java.util.HashMap;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class CustomUrlNoRightHandler implements ServerAccessDeniedHandler{
    @Override
    public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException denied) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.FORBIDDEN);
        response.getHeaders().add("Content-Type", "application/json; charset=UTF-8");

        log.info("url no right: {}", exchange.getRequest().getPath());

        HashMap<String, String> map = new HashMap<>();
        map.put("code", "-1002");
        map.put("message", "资源路径无访问权限!");
        DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONBytes(map));
        return response.writeWith(Mono.just(dataBuffer));
    }
}

2.4.5 自定义成功退出登录的回调ServerLogoutSuccessHandler

用于自定义退出登录是的返回数据格式,示例代码如下:

package com.cloudservice.gateway_service.security;
import java.util.HashMap;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class CustomLogoutSuccessHandler implements ServerLogoutSuccessHandler {@Override
    public Mono<Void> onLogoutSuccess(WebFilterExchange exchange, Authentication authentication) {
        ServerHttpResponse response = exchange.getExchange().getResponse();
        response.setStatusCode(HttpStatus.OK);
        response.getHeaders().add("Content-Type", "application/json; charset=UTF-8");

        log.info("user logout success: {}", authentication.getName());

        HashMap<String, String> map = new HashMap<>();
        map.put("code", "0");
        map.put("message", "退出登录成功!");
        DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONBytes(map));
        return response.writeWith(Mono.just(dataBuffer));
    }
}

2.4.6 最终配置类引用上述自定义回调的修改

package com.cloudservice.gateway_service.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
@Configuration
@EnableWebFluxSecurity
public class GatewaySecurityConfig {
  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }
  @Autowired
  private GatewayReactiveAuthorizationManager gatewayReactiveAuthorizationManager;
  @Autowired
  private CustomLoginFailureHandler customLoginFailureHandler;
  @Autowired 
  private CustomLoginSuccessHandler customLoginSuccessHandler;
  @Autowired
  private CustomNoLoginHandler customNoLoginHandler;
  @Autowired
  private CustomLogoutSuccessHandler customLogoutSuccessHandler;
  @Autowired
  private CustomUrlNoRightHandler customUrlNoRightHandler;
  @Bean
  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
      .authorizeExchange()
        .pathMatchers("/favicon.ico", "/login", "/logout").permitAll()
        .anyExchange().access(gatewayReactiveAuthorizationManager)
      .and().formLogin()
      .authenticationFailureHandler(customLoginFailureHandler)
      .authenticationSuccessHandler(customLoginSuccessHandler)
      .and().exceptionHandling()
        .accessDeniedHandler(customUrlNoRightHandler)
        .authenticationEntryPoint(customNoLoginHandler)
      .and().logout().logoutSuccessHandler(customLogoutSuccessHandler)
      .and().csrf().disable();
    return http.build();
  }
}

【注意】

1. 登录会话的超时配置,可以修改nacos配置,加上:

server:
  reactive:
    session:
      timeout: 1m # session超时时间为1分钟, 默认是60分钟

2. 为了防止集中登录导致的登录数据库过载,修改GatewayReactiveUserDetailsService类的findByUsername接口实现,引入redis,优先查看redis缓存里有没有用户信息,有则从redis中加载,否则才从mysql数据库中加载

[上一篇]从零开始搭建高负载java架构(04)——gateway网关节点(动态路由)

[下一篇]从零开始搭建高负载java架构(06):gateway网关节点(sentinel篇)

参考资料:

[1]Spring Security详细讲解(JWT+SpringSecurity登入案例)

[2]Spring Security核心类简介

[3]在 webflux 环境中使用 Spring Security

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ALU(算术逻辑单元)是计算机处理器中负责执行算术和逻辑操作的核心组件。在设计RISC-V处理器的过程中,ALU的优化是至关重要的一步。 首先,我们可以通过增加多个ALU管道来提处理器的性能。这样,指令可以在多个ALU之间并行执行,从而加快处理速度。另外,还可以采用超标量技术,将多个ALU分组,并行执行不同的指令,进一步提处理器效率。 其次,为了提ALU的性能,我们可以通过增加更多的功能单元来支持多种操作。例如,我们可以添加浮点运算单元,以支持浮点运算指令。此外,还可以添加位移单元和乘法单元等,以执行各种操作。通过提供更多的功能单元,ALU可以在单个周期内执行更多的操作,提处理器的性能。 另外,在ALU的设计中,还需要考虑到电路延迟的问题。通过优化电路布线和信号传输路径,可以减少ALU操作所需的时间。此外,采用流水线技术也可以提处理器的吞吐量。通过将ALU操作划分为多个阶段,并行执行多条指令,可以大大提处理器的效率。 最后,为了优化ALU的设计,还需要考虑功耗的问题。通过采用低功耗电路设计和节能技术,可以有效降低ALU的功耗。此外,在处理器的设计过程中,还可以采用动态电压调整和时钟频率调整等技术,根据实际负载情况动态调整ALU的电压和频率,以达到节能的目的。 综上所述,通过增加ALU管道、添加更多功能单元、优化电路延迟、采用流水线技术和优化功耗等方法,可以对RISC-V处理器中的ALU进行优化,提处理器的性能和效率。这些优化措施能够使处理器更快、更强大,更适应各种复杂的计算任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好的兆头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值