SpringCloud +JWT工具类+ gateway网关+Nacos 完成分布式架构的单点登录

什么是单点登录?

单点登录,简称SSO,在分布式架构项目中,只需要在一个节点上进行登录验证,就能够实现其它所有节点的访问。

SpringCloud +JWT工具类+ gateway网关+Nacos 完成分布式架构的单点登录_第1张图片

          SpringCloud +JWT工具类+ gateway网关+Nacos 完成分布式架构的单点登录_第2张图片

 1.创建一个springboot项目,在它基础上new一个module

主项目的依赖:包含了一些配置,可以直接贴贴到主项目的pom.xml里

4.0.0
    pom
    
        common_api
    
    
        org.springframework.boot
        spring-boot-starter-parent
        2.3.4.RELEASE
         
    

    com.blb
    java0307s4
    0.0.1-SNAPSHOT
    java0307s4
    Demo project for Spring Boot

    
        1.8
        Hoxton.SR8
        2.2.5.RELEASE
    
    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring.cloud-version}
                pom
                import
            
            
                com.alibaba.cloud
                spring-cloud-alibaba-dependencies
                ${spring-cloud-alibaba.version}
                pom
                import
            
        
    

    
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

接着就是用户服务的创建了,我们通常将实体类写到一个新的maven项目中,我们同样new一个module-- Common_api在里面配置SpringCloud +JWT工具类+ gateway网关+Nacos 完成分布式架构的单点登录_第3张图片

这是我配置的user实体类;我们还需要一个userTokenVO类如下:

SpringCloud +JWT工具类+ gateway网关+Nacos 完成分布式架构的单点登录_第4张图片

user服务的依赖:

4.0.0
    
        com.blb
        java0307s4
        0.0.1-SNAPSHOT
    
    com.blb.lb
    user_service
    0.0.1-SNAPSHOT
    user_service
    Demo project for Spring Boot
    
        1.8
    
    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-config
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            mysql
            mysql-connector-java
            8.0.29
        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.0.1
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
            com.blb
            0.0.1-SNAPSHOT
            common_api
        
        
            org.springframework.boot
            spring-boot-starter-security
        
    

 我们通过springboot自带的springSecurity里的UserDetailsServiceImpl 实现登录,直接在service层创建实现类:启动类加上注解:

@MapperScan("com.blb.lb.user_service.mapper")
@EnableDiscoveryClient
@SpringBootApplication
package com.blb.lb.user_service.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.blb.common.entity.User;
import com.blb.lb.user_service.mapper.UserMapper;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Resource
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("1111111111111111111");
        User user = userMapper.selectOne(new QueryWrapper().lambda().eq(User::getUsername, username));
        System.out.println(user);
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        return new org.springframework.security.core.userdetails.User(username, user.getPassword()
                , AuthorityUtils.commaSeparatedStringToAuthorityList(""));
    }
}

配置 LoginSuccessHandler

package com.blb.lb.user_service.config;


import com.blb.common.util.JwtUtil;
import com.blb.common.util.ResponseResult;
import com.blb.common.util.RsaUtil;
import com.blb.lb.user_service.entity.UserTokenVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        User user = (User) authentication.getPrincipal();
        String token = JwtUtil.generateToken(user.getUsername(), RsaUtil.privateKey, JwtUtil.EXPIRE_MINUTES);
        System.out.println(token);
        UserTokenVO userTokenVO = new UserTokenVO(user.getUsername(), token);
        ResponseResult.write(response, ResponseResult.ok(userTokenVO));
        log.info("user:{} token:{}", user.getUsername(), token);
    }
}

配置 SecurityConfig 

package com.blb.lb.user_service.config;

import com.blb.common.util.ResponseResult;
import com.blb.common.util.ResponseStatus;
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.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private LoginSuccessHandler loginSuccessHandler;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //配置自定义登录逻辑
        auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置放行url
        http.authorizeRequests()
                .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs"
                        , "/login", "/logout").permitAll()
                .anyRequest().authenticated()               //配置其它url要验证
                .and()
                .formLogin()                                //配置登录相关
                .successHandler(loginSuccessHandler)  //配置登录成功的处理器
                .failureHandler((req, resp, auth) -> {        //配置登录失败的处理器
                    ResponseResult.write(resp, ResponseResult.error(ResponseStatus.LOGIN_ERROR));
                })
                .and()
                .exceptionHandling()
                .authenticationEntryPoint((req, resp, auth) -> { //配置拦截未登录请求的处理
                    ResponseResult.write(resp, ResponseResult.error(ResponseStatus.AUTHENTICATE_ERROR));
                })
                .and()
                .logout()
                .logoutSuccessHandler((req, resp, auth) -> {     //配置登出处理器
                    ResponseResult.write(resp, ResponseResult.ok("注销成功"));
                })
                .clearAuthentication(true)                     //清除验证缓存
                .and()
                .csrf()
                .disable()                                    //关闭csrf保护
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS); //不使用session

    }
}

启动类加注解:

@MapperScan("com.blb.lb.user_service.mapper")
@EnableDiscoveryClient
@SpringBootApplication

然后我们登录需要的一些工具类放在了刚刚的Common_api中依赖:


        java0307s4
        com.blb
        0.0.1-SNAPSHOT
    
    4.0.0

    0.0.1-SNAPSHOT
    com.blb
    common_api
    
        
            com.baomidou
            mybatis-plus-boot-starter
            3.0.1
        
        
            javax.servlet
            javax.servlet-api
        
        
            io.jsonwebtoken
            jjwt
            0.9.0
        
        
            joda-time
            joda-time
            2.9.9
        
    

JWT工具类:

package com.blb.common.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;

import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * JWT工具类
 */
public class JwtUtil {

    public static final String JWT_KEY_USERNAME = "username";
    public static final int EXPIRE_MINUTES = 120;

    /**
     * 私钥加密token
     */
    public static String generateToken(String username, PrivateKey privateKey, int expireMinutes) {

        return Jwts.builder()
                .claim(JWT_KEY_USERNAME, username)
                .setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate())
                .signWith(SignatureAlgorithm.RS256, privateKey)
                .compact();
    }

    /**
     * 从token解析用户
     *
     * @param token
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static String getUsernameFromToken(String token, PublicKey publicKey) {
        Jws claimsJws = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
        Claims body = claimsJws.getBody();
        String username = (String) body.get(JWT_KEY_USERNAME);
        return username;
    }
}
RsaUti 工具类:
package com.blb.common.util;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * RSA工具类
 */
public class RsaUtil {

    public static final String RSA_SECRET = "blbweb@#$%"; //秘钥
    public static final String RSA_PATH = System.getProperty("user.dir")+"/rsa/";//秘钥保存位置
    public static final String RSA_PUB_KEY_PATH = RSA_PATH + "pubKey.rsa";//公钥路径
    public static final String RSA_PRI_KEY_PATH = RSA_PATH + "priKey.rsa";//私钥路径

    public static PublicKey publicKey; //公钥
    public static PrivateKey privateKey; //私钥

    /**
     * 类加载后,生成公钥和私钥文件
     */
    static {
        try {
            File rsa = new File(RSA_PATH);
            if (!rsa.exists()) {
                rsa.mkdirs();
            }
            File pubKey = new File(RSA_PUB_KEY_PATH);
            File priKey = new File(RSA_PRI_KEY_PATH);
            //判断公钥和私钥如果不存在就创建
            if (!priKey.exists() || !pubKey.exists()) {
                //创建公钥和私钥文件
                RsaUtil.generateKey(RSA_PUB_KEY_PATH, RSA_PRI_KEY_PATH, RSA_SECRET);
            }
            //读取公钥和私钥内容
            publicKey = RsaUtil.getPublicKey(RSA_PUB_KEY_PATH);
            privateKey = RsaUtil.getPrivateKey(RSA_PRI_KEY_PATH);
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }

    /**
     * 从文件中读取公钥
     *
     * @param filename 公钥保存路径,相对于classpath
     * @return 公钥对象
     * @throws Exception
     */
    public static PublicKey getPublicKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPublicKey(bytes);
    }

    /**
     * 从文件中读取密钥
     *
     * @param filename 私钥保存路径,相对于classpath
     * @return 私钥对象
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPrivateKey(bytes);
    }

    /**
     * 获取公钥
     *
     * @param bytes 公钥的字节形式
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(byte[] bytes) throws Exception {
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }

    /**
     * 获取密钥
     *
     * @param bytes 私钥的字节形式
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(byte[] bytes) throws Exception {
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePrivate(spec);
    }

    /**
     * 根据密文,生存rsa公钥和私钥,并写入指定文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     * @throws IOException
     * @throws NoSuchAlgorithmException
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        writeFile(publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    private static byte[] readFile(String fileName) throws Exception {
        return Files.readAllBytes(new File(fileName).toPath());
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
        File dest = new File(destPath);
        if (!dest.exists()) {
            dest.createNewFile();
        }
        Files.write(dest.toPath(), bytes);
    }
}
ResponseResult :
package com.blb.common.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 响应内容
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResponseResult {

    private ResponseStatus status;

    private T data;

    /**
     * 正确返回数据
     */
    public static  ResponseResult ok(T data){
        return new ResponseResult(ResponseStatus.OK,data);
    }

    /**
     * 返回错误消息
     */
    public static ResponseResult error(ResponseStatus status){
        return new ResponseResult<>(status,status.getMessage());
    }

    /**
     * 返回错误消息
     */
    public static ResponseResult error(ResponseStatus status,String err){
        return new ResponseResult<>(status,err);
    }

    /**
     * 将数据转换为json,发送给前端
     * @param resp
     * @param result
     */
    public static void write(HttpServletResponse resp,ResponseResult result) throws IOException {
        resp.setContentType("application/json;charset=UTF-8");
        String s = new ObjectMapper().writeValueAsString(result);
        PrintWriter writer = resp.getWriter();
        writer.write(s);
        writer.close();
    }
}
ResponseStatus :
package com.blb.common.util;

public enum ResponseStatus {

    OK(200,"请求成功"),
    INTERNAL_ERROR(500000,"内部错误"),
    LOGIN_ERROR(500001,"账号或密码错误"),
    BUSINESS_ERROR(500002,"业务错误"),
    AUTHORITY_ERROR(500003,"授权错误"),
    AUTHENTICATE_ERROR(403,"验证错误,需要登录");

    //响应代码
    private Integer code;
    //响应信息
    private String message;

    public Integer getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    ResponseStatus(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

在添加完工具类和响应类后;

通常完成  application.properties后直接启动就可以了。还有前端的登录拦截器:

这是在main.js里的 验证token的过滤器

axios.interceptors.request.use(
    config => {
        let token = localStorage.getItem("token");
        console.log("token:" + token);
        if (token) {
            //把localStorage的token放在Authorization里
            config.headers.Authorization = token;
            console.log(config.headers.Authorization )
        }
        return config;
    },
    function (err) {
        console.log("失败信息" + err);
    }
);

//错误响应拦截
axios.interceptors.response.use(res => {
    console.log('拦截响应');
    console.log(res);
    if (res.data.status === 'OK') {
        return res;
    }
    if (res.data.data === '验证错误,需要登录') {
        console.log('验证错误,需要登录')
        // window.location.href = '/'
        MessageBox.alert('没有权限,需要登录', '权限错误', {
            confirmButtonText: '跳转登录页面',
            callback: action => {
                window.location.href = '/'
            }
        })
    } else {
        Message.error(res.data.data)
    }
})

改路由:SpringCloud +JWT工具类+ gateway网关+Nacos 完成分布式架构的单点登录_第5张图片

 接着是前端的login.vue 主要代码

然后我们new一个springboot项目为gateway网关:依赖;

 4.0.0
    
        com.blb
        java0307s4
        0.0.1-SNAPSHOT
    
    com.blb
    gateway_service
    0.0.1-SNAPSHOT
    gateway_service
    Demo project for Spring Boot
    
        1.8
    
    
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-config
        
        
            com.blb
            0.0.1-SNAPSHOT
            common_api
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
            org.springframework.cloud
            spring-cloud-starter-gateway
        
        
            org.springframework.cloud
            spring-cloud-starter-bootstrap
            3.0.1
        

    

先配置白名单:

package com.blb.gateway_service.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Data
@Configuration
@ConfigurationProperties(prefix = "user")
public class WhileListConfig {
    private List whiteList;
}

接着配置过滤器:AuthenticationFilter

package com.blb.gateway_service.fliter;


import com.blb.common.util.JwtUtil;
import com.blb.common.util.RsaUtil;
import com.blb.gateway_service.config.WhileListConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * 用户验证过滤器
 */
@Slf4j
@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {

    @Autowired
    private WhileListConfig whiteListConfig;

    @Override
    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获得请求和响应对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //对白名单中的地址放行
        List whiteList = whiteListConfig.getWhiteList();
        for(String str : whiteList){
            if(request.getURI().getPath().contains(str)){
                log.info("白名单,放行{}",request.getURI().getPath());
                return chain.filter(exchange);
            }
        }
        //获得请求头中Authorization token信息
        String token = request.getHeaders().getFirst("Authorization");
        System.out.println(token);
        try{
            //解析token
            String username = JwtUtil.getUsernameFromToken(token, RsaUtil.publicKey);
            log.info("{}解析成功,放行{}",username,request.getURI().getPath());
            return chain.filter(exchange);
        }catch (Exception ex){
            log.error("token解析失败",ex);
            //返回验证失败的响应信息
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            DataBuffer wrap = response.bufferFactory().wrap("验证错误,需要登录".getBytes());
            return response.writeWith(Mono.just(wrap));
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

再在启动类上添加注解:

@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

接着就是服务的resource下的properties配置了。我将它配置到了Nacos上,实现自动刷新;

Nacos是springcloudAlibaba的组件,我们用这个就不用配置注册中心了。

  1. 下载Nacos
     下载地址https://github.com/alibaba/nacos/releases
  2. 复制文件到Linux的/usr/local目录下
     cd /usr/local
     tar -vxf nacos-server-1.4.0.tar.gz
     cd nacos/bin
     sh startup.cmd -m standalone 
     PS:启动文件位于nacos的bin目录下,cmd以管理员身份运行,再进入到nacos所在的bin目录这里以单机模式启动,除此还有集群模式

SpringCloud +JWT工具类+ gateway网关+Nacos 完成分布式架构的单点登录_第6张图片

  3. 打开浏览器,输入http://Linux主机IP:8848/nacos/index.html,就可以看到Nacos的登录界面

进入登录界面,账号密码都是nacos

SpringCloud +JWT工具类+ gateway网关+Nacos 完成分布式架构的单点登录_第7张图片

 然后进行配置:

SpringCloud +JWT工具类+ gateway网关+Nacos 完成分布式架构的单点登录_第8张图片

注意服务名称要和idea里的一样,以及文件后缀名,服务名-dev.yaml/properties,后面是根据idea里的配置文件后缀名决定。

下面是具体配置:

server:
  port: 9000
# 网关配置
spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      routes: # 路由
        - id: order-service-route
          uri: lb://order-service # 服务名称
          predicates: # 断言
            - Path=/order/**,/orders/** # 匹配路径
        - id: product-service-route
          uri: lb://product-service
          predicates:
            - Path=/product/**,/products/**
        - id: user-service-route
          uri: lb://user-service
          predicates:
            - Path=/login,/logout,/user/**
      globalcors:
        cors-configurations: # 跨域配置
          '[/**]': # 匹配所有路径
            allowed-origins: # 允许的域名
              - "http://localhost:8080"
            allowed-headers: "*" # 允许的请求头
            allowed-methods: "*" # 允许的方法
            allow-credentials: true # 是否携带cookie
user:
  white-list: # 自定义白名单
    - /login
    - /logout

以及idea里的配置文件;

spring:
  application:
    name: gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        server-addr: 127.0.0.1:8848
        prefix: gateway-service
        file-extension: yaml

  profiles:
    active: dev

yaml需要注意缩进 不要配置会失效!!

还有user服务的配置:

server.port=8082
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db4?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

mybatis-plus.type-aliases-package=com.blb.common.entity
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.mapper-locations=classpath:mapper/*.xml

上面的数据库账号密码需要根据自己的来设置,以及实体类的包路径。

接着是user服务idea里的配置:

spring.application.name=user-service


# 注册nacos
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

# nacos配置中心地址
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
# 配置文件的前缀
spring.cloud.nacos.config.prefix=user-service
# 后缀
spring.cloud.nacos.config.file-extension=properties
# profile
spring.profiles.active=dev

#开启全部端点
management.endpoints.web.exposure.include=*

其它的服务配置类似,启动网关服务和用户服务后即可实现单点登录;

这是我写单点登录的一点总结,有任何问题希望留言探讨,看到会第一时间回复,谢谢。

你可能感兴趣的:(java,springcloud,spring,cloud,gateway,分布式)