微服务 spring gateway+nacos +security+JWT 认证授权


前言:
版本信息:

  • springboot: 2.3.0.RELEASE
  • Spring Cloud Hoxton: Hoxton.SR3
  • Spring Cloud Alibaba: 2.2.1.RELEASE

一.涉及到的微服务模块以及技术有:

1.微服务

  1. 管理员资源服务
  2. app用户资源服务
  3. 网关
  4. 认证中心
  5. 公共资源服务

2.技术或组件

  1. spring gateway----网关
  2. alibaba nacos -----注册中心
  3. spring security ------认证授权
  4. jwt ---------------------生成token
  5. mybatis plus ---------持久层框架
  6. my sql -----------------数据存储
  7. Feign -----------------服务间的远程调用

二.实现的功能:

1.网关

统一系统接口入口,在网关进行鉴权操作,
(取出token中的权限信息进行鉴权)
默认负载均衡策略为:轮询策略

2.接口设计

2.1 三组资源:

  1. admin资源,
  2. 用户资源,
  3. 公共资源
(1).admin资源:

自然是只有admin角色才可访问

(2).用户资源:

分别有

  1. 用户访问资源
  2. admin访问资源(需要有admin角色可访问)
(3).公共资源

略…

3.认证中心

进行登录用户的认证授权,返回用户名和token的json数据,
权限信息放在token中

4.其他服务

顾名思义

5.接口url设计
注:
app接口需要拥有user角色的用户才可访问
admin接口需要拥有admin角色才可访问

app接口: /前缀/api-服务名/v1/api/**
admin接口: /前缀/api-服务名/v1/admin/**

5.项目服务
项目结构

三.数据表设计

数据表设计

四:代码实现

1.项目父工程

(1).依赖


    <properties>
        <java.version>1.8</java.version>
    </properties>
    <packaging>pom</packaging>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <!-- Spring Cloud Hoxton -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- Spring Cloud Alibaba -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

2.网关

网关

(1).依赖


    <dependencies>
        <!--项目公共模块-->
        <dependency>
            <groupId>com.qiongqi.taoyiquan</groupId>
            <artifactId>taoyiquan-common</artifactId>
            <version>1.0.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.security</groupId>
                    <artifactId>spring-security-jwt</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.validation</groupId>
                    <artifactId>validation-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>

    </dependencies>

(2).yml配置


server:
  port: 8849

spring:
  application:
    name: taoyiquan-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      routes:
        - id: taoyiquan-admin
          uri: lb://taoyiquan-admin # lb: 使用负载均衡策略  默认应该是轮询策略
          predicates:
            - Path=/admin/**
          filters:
            - StripPrefix=1
        - id: taoyiquan-user
          uri: lb://taoyiquan-user
          predicates:
            - Path=/user/**
          filters:
            - StripPrefix=1
        - id: taoyiquan-auth
          uri: lb://taoyiquan-auth
          predicates:
            - Path=/auth/**
          filters:
            - StripPrefix=1
        - id: taoyiquan-taobao
          uri: lb://taoyiquan-taobao
          predicates:
            - Path=/taobao/**
          filters:
            - StripPrefix=1


(3).security配置

/**
 * @description: security配置
 * @author: ※狗尾巴草
 * @date: 2020-11-26 12:01
 **/
@EnableWebFluxSecurity
public class SecurityConfig {
    @Autowired
    private CustomizeAuthenticationEntryPoint authenticationEntryPoint;

    //security的鉴权排除的url列表
    private static final String[] excludedAuthPages = {
            "/auth/api-auth/v1/login",//登录
    };

    @Bean
    SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http){
        http
                .authorizeExchange()
                // 需要权限访问de接口
                .pathMatchers("/admin/**","/user/api-user/*/admin/**").access(new XTReactiveAuthorizationManager("admin"))
                .pathMatchers("/user/api-user/*/api/**").access(new XTReactiveAuthorizationManager("admin","user"))

                //无需权限访问
//                .pathMatchers(excludedAuthPages).permitAll()  //无需进行权限过滤的请求路径
//                .pathMatchers(HttpMethod.OPTIONS).permitAll() //option 请求默认放行
                .anyExchange().permitAll()//无需权限访问

                .and()
                .httpBasic()
//                .authenticationEntryPoint()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)// 匿名访问

                .and() .csrf().disable()//必须支持跨域
                .logout().disable()
        ;

        SecurityWebFilterChain chain = http.build();
        Iterator<WebFilter> weIterable = chain.getWebFilters().toIterable().iterator();
        while(weIterable.hasNext()) {
            WebFilter f = weIterable.next();
            if(f instanceof AuthenticationWebFilter) {
                AuthenticationWebFilter webFilter = (AuthenticationWebFilter) f;
                //将自定义的AuthenticationConverter添加到过滤器中
                webFilter.setServerAuthenticationConverter(new XTAuthenticationConverter());
            }
        }
        return chain;

    }

    @Bean
    public ReactiveAuthenticationManager reactiveAuthenticationManager() {
        return new ReactiveAuthenticationManagerAdapter((authentication)->{
            if(authentication instanceof XTAccountAuthentication) {
                XTAccountAuthentication gmAccountAuthentication = (XTAccountAuthentication) authentication;
                if(gmAccountAuthentication.getPrincipal() != null) {
                    authentication.setAuthenticated(true);
                    return authentication;
                } else {
                    return authentication;
                }
            } else {
                return authentication;
            }
        });
    }


}

(4).取出token中的权限信息

/**
 * @description: 取出token中的权限信息
 * @author: ※狗尾巴草
 * @date: 2020-11-27 23:12
 **/
public class XTAuthenticationConverter extends ServerFormLoginAuthenticationConverter {
    private static Logger logger = LoggerFactory.getLogger(XTAuthenticationConverter.class);

    @Override
    public Mono<Authentication> convert(ServerWebExchange exchange) {
        System.out.println("从token中获取登陆用户信息");
        //从token中获取登陆用户信息
        List<String> tokenList = exchange.getRequest().getHeaders().get("token");
        if(tokenList==null) {
            logger.error("用户认证信息为空,返回获取认证信息失败");
            return Mono.empty();
        } else {
            String token = tokenList.get(0);
            List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
            //获取权限信息
            if(!JwtTokenUtils.checkJWT(token)){
                logger.error("用户认证信息为空,返回获取认证信息失败");
                return Mono.empty();
            }
            List<String> roles = JwtTokenUtils.getUserRole(token);
            if(roles==null){
                System.out.println("token过期");
                return Mono.empty();
            }
            roles.forEach(role ->{
                SimpleGrantedAuthority auth = new SimpleGrantedAuthority(role);
                simpleGrantedAuthorities.add(auth);
            });
            //添加用户信息到spring security之中。
            XTAccountAuthentication  xinyueAccountAuthentication = new XTAccountAuthentication(null, token, simpleGrantedAuthorities);
            return Mono.just(xinyueAccountAuthentication);
        }
    }
}

XTAccountAuthentication类

/**
 * @description: 说明
 * @author: ※狗尾巴草
 * @date: 2020-11-27 23:13
 **/
public class XTAccountAuthentication extends AbstractAuthenticationToken {

    private static final long serialVersionUID = 1L;
    private Object credentials;
    private String principal;

    public XTAccountAuthentication(Object credentials,String principal,Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.credentials = credentials;
        this.principal = principal;
    }

    @Override
    public Object getCredentials() {
        return this.credentials;
    }

    @Override
    public String getPrincipal() {
        return this.principal;
    }
}

(5).鉴权

/**
 * @description: 鉴权
 * @author: ※狗尾巴草
 * @date: 2020-11-27 21:46
 **/
public class XTReactiveAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext>  {

    private List<String> authorities = new ArrayList<>();

    public XTReactiveAuthorizationManager(String authority, String... authorities ) {
        this.authorities.add("ROLE_" + authority);
        if(authorities != null) {
            for(String auth : authorities) {
                this.authorities.add("ROLE_" + auth);
            }
        }
    }

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {

        return authentication
                .filter(a -> a.isAuthenticated())
                .flatMapIterable(a -> a.getAuthorities())
                .map(g -> g.getAuthority())
                .any(c -> {
                    //检测权限是否匹配
                    System.out.println("角色: " + c);
                    if (authorities.contains(c)) {
                        return true;
                    }
                    ResponseData<Object> data = new ResponseData<>().fail(ResultCode.NO_PERMISSION.getCode(),ResultCode.NO_PERMISSION.getMessage());
                    //权限不足,抛出异常
                    throw new AccessDeniedException(JSONObject.toJSONString(data));
                })
                .map(hasAuthority -> new AuthorizationDecision(hasAuthority))
                .defaultIfEmpty(new AuthorizationDecision(false));
    }
}

(6).统一异常捕获

/**
 * @description: 网关异常通用处理器,只作用在webflux 环境下
 * @author: ※狗尾巴草
 * @date: 2020-11-28 16:28
 **/
@Order(-1)
@RequiredArgsConstructor
@Component
public class GlobalExceptionConfiguration implements ErrorWebExceptionHandler {
    private final ObjectMapper objectMapper;

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        System.out.println("发生异常");
        ServerHttpResponse response = exchange.getResponse();

        if (response.isCommitted()) {
            return Mono.error(ex);
        }

        //设置响应头的数据类型,applicaion/json模式,返回json数据
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        if (ex instanceof ResponseStatusException) {
            response.setStatusCode(((ResponseStatusException) ex).getStatus());
        }

        return response
                .writeWith(Mono.fromSupplier(() -> {
                    DataBufferFactory bufferFactory = response.bufferFactory();
                    try {
                        return bufferFactory.wrap(objectMapper.writeValueAsBytes(ex.getMessage()));
                    } catch (JsonProcessingException e) {
//                        log.warn("Error writing response", ex);
                        return bufferFactory.wrap(new byte[0]);
                    }
                }));
    }
}

(7).无权限处理


/**
 * @description: 匿名用户访问无权限资源时的异常
 * @author: ※狗尾巴草
 * @date: 2020-11-13 14:15
 **/
@Component
public class CustomizeAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {


    @Override
    public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {
        System.out.println("无权限访问");
        ResponseData<Object> data = new ResponseData<>().fail(ResultCode.NO_PERMISSION.getCode(),ResultCode.NO_PERMISSION.getMessage());
//        抛出异常
        return Mono.error(new  AccessDeniedException(JSONObject.toJSONString(data)));
    }
}

3.认证中心

注:
统一认证中心,统一登录入口,同一套规则,但是管理员数据和普通用户数据还是分表存储,暂时没找到好的办法解决,这里使用简单的方法区别管理员登录和普通用户登录,增加前缀,
例如
管理员登录,admin_111
用户登录: user_111

认证中心

(1).依赖


    <dependencies>
        <!--公共模块-->
        <dependency>
            <groupId>com.qiongqi.taoyiquan</groupId>
            <artifactId>taoyiquan-common</artifactId>
            <version>1.0.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.security</groupId>
                    <artifactId>spring-security-jwt</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.validation</groupId>
                    <artifactId>validation-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--springcloud整合的openFeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>
    </dependencies>

(2).yml配置

server:
  port: 8850
  servlet:
    context-path: /api-auth

spring:
  application:
    name: taoyiquan-auth
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

(3).Security配置


/**
 * @description: Security配置
 * @author: ※狗尾巴草
 * @date: 2020-11-12 12:25
 **/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomizeAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private CustomizeAuthenticationSuccessHandler successHandler;

    @Autowired
    private CustomizeAuthenticationFailureHandler failureHandler;

    @Autowired
    private CustomizeLogoutSuccessHandler logoutSuccessHandler;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        // 设置默认的加密方式(强hash方式加密)
        return new BCryptPasswordEncoder();
    }


    @Bean
    public UserDetailsService userDetailsService() {
        //获取用户账号密码及权限信息
        return new UserDetailsServiceImpl();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //配置认证方式
        auth.userDetailsService(userDetailsService());
    }

//    @Override
//    public void configure(WebSecurity web) {
//        //对于在header里面增加token等类似情况,放行所有OPTIONS请求。
//        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
//    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //http相关的配置,包括登入登出、异常处理、会话管理等
        http
        .authorizeRequests()
//                .antMatchers("/user/**").hasAuthority("query_user")
//                基于角色控制
//                .antMatchers("/admin/**").hasAnyRole("admin")
//                .antMatchers("/user/**").hasAnyRole("user")
        //登入
        .and()
                .formLogin()
                .loginPage("/v1/login")
                .permitAll()//允许所有用户
                .successHandler(successHandler)//登录成功处理逻辑
                .failureHandler(failureHandler)//登录失败处理逻辑
//        //登出
        .and().logout()
                .permitAll()//允许所有用户
                .logoutSuccessHandler(logoutSuccessHandler)//登出成功处理逻辑
                .deleteCookies("JSESSIONID")//登出之后删除cookie
                //异常处理(权限拒绝、登录失效等)
        .and().exceptionHandling().
                authenticationEntryPoint(authenticationEntryPoint)//匿名用户访问无权限资源时的异常处理
//                .accessDeniedHandler(accessDeniedHandler)//异常捕获
//                .authenticationEntryPoint(new JWTAuthenticationEntryPoint())
//        限制同一账号只能一个用户使用  会话管理
        .and().sessionManagement()
//                .maximumSessions(1)//同一账号同时登录最大用户数
//                .expiredSessionStrategy(sessionInformationExpiredStrategy)//会话信息过期策略会话信息过期策略(账号被挤下线)
        ;
        http.csrf().disable().cors()
                .and()
//                .addFilter(new JWTAuthenticationFilter(authenticationManager()))
//                .addFilter(new JWTAuthorizationFilter(authenticationManager()))
                // 不需要session
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        ;
//        http.addFilterBefore(securityInterceptor, FilterSecurityInterceptor.class);//增加到默认拦截链中
    }
}

(4).登录认证授权

/**
 * @description: 登录认证授权
 * @author: ※狗尾巴草
 * @date: 2020-11-13 10:00
 **/
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private adminFeignClient adminFeignClient;

    @Autowired
    private roleFeignClient roleFeignClient;

    @Autowired
    private userFeignClient userFeignClient;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //需要构造出 org.springframework.security.core.userdetails.User 对象并返回
        //校验验证码等操作....
        //
        if(!StringUtils.isNotBlank(username)){
            throw new RuntimeException("用户不能为空");
        }
        ResponseData Account =null;
                String[] name_list = username.split("_");
        if("user".equals(name_list[0])){
//            普通用户登录
            Account = userFeignClient.getByAccount(name_list[1]);

        }else if("admin".equals(name_list[0])){
//            管理员登录
            Account = adminFeignClient.getByAccount(name_list[1]);
        }
        LinkedHashMap data = (LinkedHashMap) Account.getData();
        if(data==null){
            throw new RuntimeException("用户不存在");
        }
        List<GrantedAuthority> grantedAuthorities  = new ArrayList<>();
        //获取该用户所拥有的角色
        ResponseData roleData = roleFeignClient.getByUserId(data.get("id").toString());
        JSONObject roleJson =(JSONObject) JSONObject.toJSON(roleData);

        //角色和权限共用GrantedAuthority接口,唯一的不同角色就是多了个前缀"ROLE_"

//        // 声明用户角色
        roleJson.getJSONArray("data").forEach(role ->{
            //基于角色控制
            grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_"+((JSONObject)role).get("roleCode")));
        });

        User user = new User(data.get("account").toString(), data.get("password").toString(), true, true, true, true, grantedAuthorities);
        return user;
    }

}

(5).登录成功处理器


/**
 * @description: 登录成功处理器
 * @author: ※狗尾巴草
 * @date: 2020-11-13 14:19
 **/
@Component
public class CustomizeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    private adminFeignClient adminFeignClient;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        //更新用户表上次登录时间、更新人、更新时间等字段
        User userDetails = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        adminFeignClient.updateLoginTime(userDetails.getUsername(),new Date());

//        SysUserEntity sysUser = sysUserService.selectByName(userDetails.getUsername());
//        sysUser.setLast_login_time(new Date());
//        sysUser.setUpdate_time(new Date());
//        sysUser.setUpdate_user(sysUser.getId());
//        sysUserService.updateById(sysUser);

        //此处还可以进行一些处理,比如登录成功之后可能需要返回给前台当前用户有哪些菜单权限,
        //进而前台动态的控制菜单的显示等,具体根据自己的业务需求进行扩展

        //  获取用户权限
        Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
        List<String> list = new ArrayList<>();
        for (GrantedAuthority authority : authorities){
            list.add(authority.getAuthority());
        }
        //将权限放入token中
        String token = JwtTokenUtils.createToken(userDetails.getUsername(), list);
        JSONObject object = new JSONObject();
        object.put("token",token);
        object.put("account",userDetails.getUsername());

        //处理编码方式,防止中文乱码的情况
        httpServletResponse.setContentType("text/json;charset=utf-8");
        //塞到HttpServletResponse中返回给前台
        httpServletResponse.getWriter().write(JSON.toJSONString(object));
    }
}

(6).登录失败


/**
 * @description: 登录失败处理逻辑
 * @author: ※狗尾巴草
 * @date: 2020-11-13 14:22
 **/
@Component
public class CustomizeAuthenticationFailureHandler implements AuthenticationFailureHandler {


    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        //返回json数据
        ResponseData<Object> data = null;
        if (e instanceof AccountExpiredException) {
            //账号过期
            data = new ResponseData<>().fail(ResultCode.USER_ACCOUNT_EXPIRED.getCode(),ResultCode.USER_ACCOUNT_EXPIRED.getMessage());
        } else if (e instanceof BadCredentialsException) {
            //密码错误
            data = new ResponseData<>().fail(ResultCode.USER_CREDENTIALS_ERROR.getCode(),ResultCode.USER_CREDENTIALS_ERROR.getMessage());
        } else if (e instanceof CredentialsExpiredException) {
            //密码过期
            data = new ResponseData<>().fail(ResultCode.USER_CREDENTIALS_EXPIRED.getCode(),ResultCode.USER_CREDENTIALS_EXPIRED.getMessage());
        } else if (e instanceof DisabledException) {
            //账号不可用
            data = new ResponseData<>().fail(ResultCode.USER_ACCOUNT_DISABLE.getCode(),ResultCode.USER_ACCOUNT_DISABLE.getMessage());
        } else if (e instanceof LockedException) {
            //账号锁定
            data = new ResponseData<>().fail(ResultCode.USER_ACCOUNT_LOCKED.getCode(),ResultCode.USER_ACCOUNT_LOCKED.getMessage());
        } else if (e instanceof InternalAuthenticationServiceException) {
            //用户不存在
            data = new ResponseData<>().fail(ResultCode.USER_ACCOUNT_NOT_EXIST.getCode(),ResultCode.USER_ACCOUNT_NOT_EXIST.getMessage());
        }else{
            //其他错误
            data = new ResponseData<>().fail(ResultCode.COMMON_FAIL.getCode(),ResultCode.COMMON_FAIL.getMessage());
        }
        //处理编码方式,防止中文乱码的情况
        httpServletResponse.setContentType("text/json;charset=utf-8");
        //塞到HttpServletResponse中返回给前台
        httpServletResponse.getWriter().write(JSON.toJSONString(data));
    }
}

7.微服务调用

这里使用Feign做服务调用,

1.admin服务调用

/**
 * @description: admin服务调用
 *  * 声明需要调用的微服务名称
 *  *  @FeignClient
 *  *      * name : 服务提供者的名称
 * @author: ※狗尾巴草
 * @date: 2020-11-27 11:20
 **/
@FeignClient(name="taoyiquan-admin",contextId = "admin")
public interface adminFeignClient {

    @GetMapping("/api-admin/v1/admin/entity/getByAccount")
    ResponseData getByAccount(@RequestParam("account") String account);

    @GetMapping("/api-admin/v1/admin/entity/updateLoginTime")
    ResponseData updateLoginTime(@RequestParam("account") String account, @RequestParam("loginTime") Date loginTime);

}

2.其他服务调用参考源码,

管理员服务

管理员
注:管理员服务,用户服务等无特殊逻辑实现,跟平常些项目一样增删改查操作

(省略一堆废话…)

五.功能测试

1.普通用户登录

接口; http://localhost:8849/auth/api-auth/v1/login

普通用户登录

(1).访问接口

(a).用户资源接口-app接口

http://localhost:8849/user/api-user/v1/api/entity

用户资源接口-api接口

(b).用户资源接口-admin接口

http://localhost:8849/user/api-user/v1/admin/entity

用户资源接口-admin接口

( c) .管理员接口

http://localhost:8849/admin/api-admin/v1/admin/entity

管理员接口

(d).公共资源接口

http://localhost:8849/taobao/api-taobao/v1/info

公共资源接口

2.管理员登录

http://localhost:8849/auth/api-auth/v1/login?username=admin_111&password=111

管理员登录

1.访问接口

(a).用户资源接口-app接口

http://localhost:8849/user/api-user/v1/api/entity

用户资源接口-app接口

(b).用户资源接口-admin接口

http://localhost:8849/user/api-user/v1/admin/entity

用户资源接口-admin接口

(a).访问管理员接口

http://localhost:8849/admin/api-admin/v1/admin/entity

访问管理员接口

六:最后:

想象中的功能设计:

本来想在gateway的全局过滤器中拦截指定接口,再其判断token的有效性,有效即可通过,去鉴权,无效就拦截

现实中的功能实现(我所能实现的):

添加完成security和全局过滤器后,发现奇怪的事情,就是security执行在先,过滤器执行再后,无论过滤器的Order设置多小(记得是Order越小,优先级越高,先执行),都是过滤器执行再后,无奈就将过滤器注释掉,

七.项目地址

传送门

大体功能写到这,细节未完善

有哪位路过的大佬,还望不吝赐教

补充:之前忘记备份数据库,后来系统重装,一夜回到解放前…
根据实体类补充了数据库,已上传github,还没测(目测基本不用测,哈哈)

  • 12
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
根据提供的引用内容,springcloud nacos gateway knife的问题可能是关于如何在Spring Cloud中使用Nacos作为注册中心和配置中心,以及如何引入Knife4j的相关依赖的。下面是一个可能的回答: 在你的Spring Cloud项目中,你可以通过引入Nacos的相关依赖来将Nacos作为注册中心和配置中心。具体来说,你需要在pom.xml中引入以下依赖: ```xml <!-- Nacos注册中心依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>${nacos.version}</version> </dependency> <!-- Nacos配置中心依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>${nacos.version}</version> </dependency> ``` 请在pom.xml中添加上述依赖以启用Nacos作为注册中心和配置中心。 另外,如果你还想在服务和网关中使用Knife4j,你需要在pom.xml中引入以下依赖: ```xml <!-- Knife4j依赖 --> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>2.0.3</version> </dependency> ``` 请在pom.xml中添加上述依赖以启用Knife4j。 此外,你还需要在配置文件中进行相应的配置。例如,你可以在application.yml或application.properties中添加以下配置: ```yaml spring: profiles: active: dev application: name: bssp-gateway-service cloud: nacos: config: server-addr: 127.0.0.1:8848 namespace: 3a44ef28-b35c-44bb-8cd2-873c14ebc911 group: DEFAULT_GROUP file-extension: yml discovery: server-addr: 127.0.0.1:8848 namespace: 3a44ef28-b35c-44bb-8cd2-873c14ebc911 gateway: discovery: locator: enabled: true ``` 请根据你的具体需求修改上述配置,确保Nacos和Knife4j的配置正确。 希望以上信息对你有帮助。如果还有其他问题,请随时提问。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值