畅购第10天项目总结(资源服务器授权以及OAuth2对接微服务)

199 阅读15分钟

1. 资源服务器授权配置

1.1 资源服务授权配置

在这里插入图片描述

基本上所有微服务都是资源服务。

1. 资源服务中配置公钥

  • 认证服务生成令牌采用非对称加密算法,认证服务采用私钥加密生成令牌,对外向资源服务提供公钥,资源服务使用公钥来校验令牌的合法性。 将公钥拷贝到 public.key 文件中,将此文件拷贝到每一个需要的资源服务工程的classpath(resources目录)下,eg: 用户微服务:

2. 资源服务中添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

3. 配置每个系统的Http请求路径安全控制策略以及读取公钥信息识别令牌,如下:

/**
 * @Auther: csp1999
 * @Date: 2021/01/26/19:41
 * @Description: 资源授权配置(访问该微服务时,需要校验令牌)
 */
@Configuration
@EnableResourceServer // 开启资源校验服务 ---> 令牌校验
// 开启全局方法安全校验(方法权限控制:不同权限的登录用户,可以访问的方法不同)
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)// 激活方法上的PreAuthorize注解
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    /**
     * 公钥
     */
    private static final String PUBLIC_KEY = "public.key";

    /**
     * 定义JwtTokenStore
     *
     * @param jwtAccessTokenConverter
     * @return
     */
    @Bean
    public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
        return new JwtTokenStore(jwtAccessTokenConverter);
    }

    /**
     * 定义JJwtAccessTokenConverter
     *
     * @return
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setVerifierKey(getPubKey());
        return converter;
    }

    /**
     * 获取非对称加密的公钥Key
     *
     * @return 公钥 Key
     */
    private String getPubKey() {
        Resource resource = new ClassPathResource(PUBLIC_KEY);
        try {
            InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream());
            BufferedReader br = new BufferedReader(inputStreamReader);
            return br.lines().collect(Collectors.joining("\n"));
        } catch (IOException ioe) {
            return null;
        }
    }

    /**
     * Http安全配置,对每个到达系统的http请求链接进行校验
     *
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 所有请求必须认证通过
        http.authorizeRequests()
                // 下边的路径放行
                // 放行的请求路径不用登陆也可以访问
                .antMatchers(
                        "/user/add","/user/load/*"). // 配置地址放行
                permitAll()
                .anyRequest().
                authenticated();    // 其他地址需要认证授权
    }
}

1.2 用户微服务资源授权

1.将生成的公钥public.key拷贝到changgou-service-user微服务工程的resources目录下,如下图:

在这里插入图片描述

2.引入依赖:

  • 在changgou-service-user微服务工程pom.xml中引入上面的oauth2.0依赖

3.资源授权配置:

  • 在changgou-service-user工程中创建com.changgou.user.config.ResourceServerConfig(上边有)

1.3 授权测试

在这里插入图片描述

用户每次访问微服务的时候,需要先申请令牌,令牌申请后,每次将令牌放到头文件中,才能访问微服务。

头文件中每次需要添加一个Authorization头信息,头信息的内容格式为:bearer token(token为申请得到的令牌)。

1.不携带令牌测试:

访问http://localhost:8089/user 不携带令牌,结果如下:

在这里插入图片描述

2.携带正确令牌访问

先通过认证微服务执行登录,拿到令牌:

访问localhost:9001/user/login?username=szitheima&password=szitheima获取token令牌,结果如下:

在这里插入图片描述

访问http://localhost:8089/user/szitheima(携带正确令牌),根据username查询用户信息

在这里插入图片描述

执行后结果如下:

在这里插入图片描述

3.携带错误令牌

访问http://localhost:18089/user/szitheima携带不正确的token令牌,结果如下:

在这里插入图片描述

2. OAuth2对接微服务

在这里插入图片描述

用户每次访问微服务的时候,先去oauth2.0服务登录,登录后再访问微服务网关,微服务网关将请求转发给其他微服务处理。

步骤:

  • 1.用户登录成功后,会将令牌信息存入到cookie中(一般建议存入到头文件中)
  • 2.用户携带Cookie中的令牌访问微服务网关
  • 3.微服务网关先获取头文件中的令牌信息,如果Header中没有Authorization令牌信息,则去参数中找,参数中如果没有,则去Cookie中找Authorization,最后将令牌信息封装到Header中,然后再调用其他微服务
  • 4.其他微服务会获取头文件中的Authorization令牌信息,然后匹配令牌数据是否能使用公钥解密,如果解密成功说明用户已登录,解密失败,说明用户未登录

2.1 令牌加入到Header中

修改changgou-gateway-web的全局过滤器com.changgou.filter.AuthorizeFilter,实现将令牌信息添加到头文件中,代码如下:

/**
 * @Auther: csp1999
 * @Date: 2021/01/24/20:17
 * @Description: 全局过滤器: 用于鉴权(获取令牌  + 解析令牌  + 判断令牌是否合法)
 */
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

    // 令牌头
    private static final String AUTHORIZE_TOKEN = "Authorization";

    /**
     * 全局过滤器
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        // 1.获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        // 2.获取响应对象
        ServerHttpResponse response = exchange.getResponse();

        // 获取请求的URI
        String path = request.getURI().getPath();

        // 3.未登录状态下只放行登录login和搜索search
        if (path.startsWith("/api/user/login") || path.startsWith("/api/brand/search/")) {
            // 直接放行
            return chain.filter(exchange);
        }

        // 4.判断是否为登录的URL 如果不是则权限校验
        // 4.1 从头header中获取令牌数据
        String token = request.getHeaders().getFirst(AUTHORIZE_TOKEN);
        // 如果为true:说明令牌在header中, false:令牌不在header中,将令牌封装入header,再传递给其他微服务
        boolean hasToken = true;

        // 如果header中没有token数据
        if(StringUtils.isEmpty(token)){
            // 4.2 从cookie中中获取令牌数据
            HttpCookie first = request.getCookies().getFirst(AUTHORIZE_TOKEN);
            if(first!=null){
                token=first.getValue();// 就是令牌的数据
            }
            // 令牌不在header中
            hasToken = false;
        }

        // 如果cookie中也没有令牌数据,则从请求参数中获取
        if(StringUtils.isEmpty(token)){
            // 4.3 从请求参数中获取令牌数据
            token= request.getQueryParams().getFirst(AUTHORIZE_TOKEN);

            // 令牌不在header中
            hasToken = false;
        }

        // 如果请求参数中仍然没有token数据
        if(StringUtils.isEmpty(token)){
            // 4.4. 则,结束,响应UNAUTHORIZED状态码405.
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        // 如果得到token数据,则:
        // 5.解析令牌数据(判断解析是否正确,正确就放行,否则就结束)
        try {
            // 1.借助jwt工具类解析token校验令牌
            // Claims claims = JwtUtil.parseJWT(token);

            // 2.借助oautho2校验令牌
            // 如果请求头中没有token:
            if (!hasToken) {
                // token令牌不为空,则判断令牌是否有bearer前缀,如果没有则添加该前缀
                if (!token.startsWith("bearer") && !token.startsWith("Bearer")) {
                    token = "bearer" + token;
                }
                // 如果有前缀,放行
                // 放行之前,将令牌封装到头文件中(这一步是为了方便AUTH2校验令牌)
                request.mutate().header(AUTHORIZE_TOKEN, token);
            }
        } catch (Exception e) {
            e.printStackTrace();
            // 解析失败,响应状态码401
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        // 放行
        return chain.filter(exchange);
    }

    /**
     * 过滤器执行顺序
     *
     * @return
     */
    @Override
    public int getOrder() {
        // 首位
        return 0;
    }
}

访问测试:

访问http://localhost:8001/api/user/szitheima,将生成的新令牌放到头文件中,在令牌前面添加Bearer,这里主要由个空格,效果如下:

在这里插入图片描述

2.2 用户身份权限控制

由于我们项目使用了微服务,任何用户都有可能使用任意微服务,此时我们需要控制相关权限,例如:普通用户角色不能使用用户的删除操作,只有管理员才可以使用,那么这个时候就需要使用到SpringSecurity的权限控制功能了。

2.2.1 角色身份定义

在changgou-user-oauth2认证微服务中,com.changgou.oauth.config.UserDetailsServiceImpl该类实现了加载用户相关信息,如下代码:

在这里插入图片描述

上述代码给登录用户定义了2个角色,分别为useradmin,这一块我们目前使用的是硬编码方式将角色写死了,后面会从数据库加载。

2.2.2 角色权限控制

在每个微服务中,需要获取用户的角色,然后根据角色识别是否允许操作指定的方法,Spring Security中定义了四个支持权限控制的表达式注解,分别是@PreAuthorize@PostAuthorize@PreFilter@PostFilter。其中前两者可以用来在方法调用前或者调用后进行权限检查,后两者可以用来对集合类型的参数或者返回值进行过滤。在需要控制权限的方法上,我们可以添加@PreAuthorize注解,用于方法执行前进行权限检查,校验用户当前角色是否能访问该方法。

1.开启@PreAuthorize

在changgou-user-service的ResourceServerConfig类上添加@EnableGlobalMethodSecurity注解,用于开启@PreAuthorize的支持,代码如下:

在这里插入图片描述

2.方法权限控制

在changgoug-service-user微服务的com.changgou.user.controller.UserController类的findAll()方法(查询所有用户信息)上添加权限控制注解@PreAuthorize,代码如下:

/**
 * 查询User全部数据
 *
 * @return
 */
//@PreAuthorize("hasAnyRole('user','admin')")
// 添加权限:只允许管理员admin角色访问,不允许其他角色访问
@PreAuthorize("hasAnyRole('admin')")
@GetMapping
public Result<List<User>> findAll() {
    // 调用UserService实现查询所有User
    List<User> list = userService.findAll();
    return new Result<List<User>>(true, StatusCode.OK, "查询成功", list);
}

3.测试用户角色权限

访问localhost:8001/api/user获取所有用户信息:

在这里插入图片描述

发现上面请求无法访问,因为用户登录的时候,角色不包含admin角色,而findAll()方法需要admin角色,所以被拦截了。

接下来再测试其他没有加admin管理员权限的方法,其他方法(eg: findById())没有配置拦截,所以用户登录后就会放行:

在这里插入图片描述

知识点说明:

如果希望一个方法能被多个角色访问,配置:@PreAuthorize("hasAnyAuthority('admin','user')")

如果希望一个都能被多个角色访问,在类上配置:@PreAuthorize("hasAnyAuthority('admin','user')")

3. OAuth2动态加载数据(从数据库获取数据)

前面OAuth2我们用的数据都是静态的,在现实工作中,数据都是从数据库加载的,所以我们需要调整一下OAuth服务,从数据库加载相关数据。

  • 客户端数据[生成令牌相关数据]
  • 用户登录账号密码从数据库加载

3.1 客户端信息相关数据加载

3.1.1 数据介绍

客户端静态数据

changgou-user-oauth的com.changgou.oauth.config.AuthorizationServerConfig类中配置了客户端静态数据,主要用于配置客户端数据,代码如下:

在这里插入图片描述

客户端表结构

创建一个数据库changgou_oauth,并在数据库中创建一张表,表主要用于记录客户端相关信息,表结构如下:

CREATE TABLE `oauth_client_details` (
  `client_id` varchar(48) NOT NULL COMMENT '客户端ID,主要用于标识对应的应用',
  `resource_ids` varchar(256) DEFAULT NULL,
  `client_secret` varchar(256) DEFAULT NULL COMMENT '客户端秘钥,BCryptPasswordEncoder加密算法加密',
  `scope` varchar(256) DEFAULT NULL COMMENT '对应的范围',
  `authorized_grant_types` varchar(256) DEFAULT NULL COMMENT '认证模式',
  `web_server_redirect_uri` varchar(256) DEFAULT NULL COMMENT '认证后重定向地址',
  `authorities` varchar(256) DEFAULT NULL,
  `access_token_validity` int(11) DEFAULT NULL COMMENT '令牌有效期',
  `refresh_token_validity` int(11) DEFAULT NULL COMMENT '令牌刷新周期',
  `additional_information` varchar(4096) DEFAULT NULL,
  `autoapprove` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

字段说明:

client_id:客户端id 
resource_ids:资源id(暂时不用) 
client_secret:客户端秘钥 
scope:范围 
access_token_validity:访问token的有效期(秒) 
refresh_token_validity:刷新token的有效期(秒) 
authorized_grant_type:授权类型:authorization_code,password,refresh_token,client_credentials    

导入2条记录到表中,SQL如下:数据中密文分别为changgou、szitheima

INSERT INTO `oauth_client_details` VALUES ('changgou', null, '$2a$10$wZRCFgWnwABfE60igAkBPeuGFuzk74V2jw3/trkdUZpnteCtJ9p9m', 'app', 'authorization_code,password,refresh_token,client_credentials', 'http://localhost', null, '432000000', '432000000', null, null);
INSERT INTO `oauth_client_details` VALUES ('szitheima', null, '$2a$10$igxoCZxTbjWx5TrmfWEEpe/WFdwbUhbxik9BKTe9i64ZOSfnu/lqe', 'app', 'authorization_code,password,refresh_token,client_credentials', 'http://localhost', null, '432000000', '432000000', null, null);

上述表结构属于SpringSecurity Oauth2.0所需的一个认证表结构,不能随意更改。相关操作在其他类中有所体现,如:org.springframework.security.oauth2.provider.client.JdbcClientDetailsService中的片段代码如下:

在这里插入图片描述

3.1.2 从库数据加载相关数据

修改连接配置

从数据库加载数据,我们需要先配置数据库连接,在changgou-user-oauth2的application.yml中配置连接信息,如下代码:

server:
  # 认证微服务端口
  port: 9001
# spring 相关配置
spring:
  application:
    # 微服务名称
    name: user-auth
  # Redis配置
  redis:
    # Redis数据库索引(默认为0)
    database: 0
    # Redis服务器地址
    host: 8.131.66.136
    # Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    password: csp19990129
  # 数据库相关配置
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/changgou_oauth?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=UTC
    username: root
    password: root
  main:
    allow-bean-definition-overriding: true
# eureka相关配置
eureka:
  instance:
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://127.0.0.1:7001/eureka
# auth相关配置
auth:
  # token存储到redis的过期时间
  ttl: 3600
  # client唯一id
  clientId: changgou
  # client密钥
  clientSecret: changgou
  # cookie域名
  cookieDomain: localhost
  cookieMaxAge: -1
# 本地证书、密钥以及证书密码配置
encrypt:
  key-store:
    # 证书路径(resources路径下)
    location: classpath:/changgou.jks
    # 密钥
    # 公钥(提供给每个微服务),可以加密,用于校验令牌的合法性--->私钥(提供给认证微服务),可以解密,用于生成令牌--->非对称加密算法RSA
    # 我们之前做的MD5加密算法,使用的是摘要加密算法,不可逆!
    # AES/DESC 使用的是对称加密,可以加密和解密,加密解密的密钥是相同的!
    secret: changgou
    # 证书别名
    alias: changgou
    # 证书密码
    password: changgou

修改客户端加载源

修改changgou-user-oauth2的com.changgou.oauth.config.AuthorizationServerConfig类的configure方法,将之前静态的客户端数据变成从数据库加载,修改如下:

/**
 * 客户端信息配置
 *
 * @param clients
 * @throws Exception
 */
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    
    // 静态客户端信息配置
    /*
    clients.inMemory()
            .withClient("changgou")          // 客户端id
            .secret("changgou")                     // 秘钥
            .redirectUris("http://localhost")       // 重定向地址
            .accessTokenValiditySeconds(3600)       // 访问令牌有效期
            .refreshTokenValiditySeconds(3600)      // 刷新令牌有效期
            .authorizedGrantTypes(
                    "authorization_code",           // 根据授权码生成令牌
                    "client_credentials",           // 客户端认证
                    "refresh_token",                // 刷新令牌
                    "password")                     // 密码方式认证
            .scopes("app");                         // 客户端范围,名称自定义,必填
    */
    // 从数据库中获取上面的数据:
    clients.jdbc(dataSource).clients(clientDetails());// 从数据库加载客户端信息
}

UserDetailsServiceImpl修改

将之前的加密方式去掉即可,代码如下:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    //====================================客户端信息认证 开始====================================
    // 取出身份,如果身份为空说明没有认证
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    // 没有认证统一采用httpbasic认证,httpbasic中存储了client_id和client_secret,开始认证client_id和client_secret
    if (authentication == null) {
        ClientDetails clientDetails = clientDetailsService.loadClientByClientId(username);
        if (clientDetails != null) {
            // 秘钥
            String clientSecret = clientDetails.getClientSecret();
            // 1.静态方式
            //return new User(
            //        username, // 客户端id
            //        new BCryptPasswordEncoder().encode(clientSecret), // 客户端密钥->加密操作
            //        AuthorityUtils.commaSeparatedStringToAuthorityList(""));// 权限
            // 2.数据库查找方式
            return new User(
                    username,// 客户端id
                    clientSecret,// 客户端密钥->不需要再加密操作,因为数据库中已经加密
                    AuthorityUtils.commaSeparatedStringToAuthorityList(""));// 权限
        }
    }
    //====================================客户端信息认证 结束====================================
    ...
}

3.1.3 测试

授权码模式测试

访问:http://localhost:9001/oauth/authorize?client_id=szitheima&response_type=code&scop=app&redirect_uri=http://localhost效果如下:

在这里插入图片描述

用户名对应应用id,密码对应秘钥。账号输入:szitheima 密码:szitheima,效果如下:

在这里插入图片描述

密码模式授权测试

我们之前编写的账号密码登录代码如下,每次都会加载指定的客户端ID和指定的秘钥,所以此时的客户端ID和秘钥固定了,输入的账号密码不再是客户端ID和秘钥了。

在这里插入图片描述

 /**
  * 登录方法:
  * 1.密码模式认证-授权方式:grant_type=password
  *
  * @param username 2.账号 szitheima
  * @param password 3.密码 szitheima
  * @return
  */
 @RequestMapping("/login")
 public Result<Map> login(String username, String password) {
     if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
         throw new RuntimeException("用户名/密码不允许为空");
     }
     // 申请令牌:调用loginService的login方法进行登录,并返回生成的令牌数据
     AuthToken authToken = loginService.login(username, password, clientId, clientSecret, GRAND_TYPE);
     // 设置到cookie中
     this.saveCookie(authToken.getAccessToken());
     
     return new Result<>(true, StatusCode.OK, "令牌生成成功", authToken);
 }
 // 保存token到cookie中
 private void saveCookie(String token) {
     HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
     CookieUtil.addCookie(response, cookieDomain, "/", "Authorization", token, cookieMaxAge, false);
 }

OAuth中的com.changgou.oauth.config.UserDetailsServiceImpl配置如下:

在这里插入图片描述

用户每次输入账号和密码,只要密码是szitheima,即可登录成功。

访问地址http://localhost:9001/user/login 输入账号密码均为szitheima,效果如下:

在这里插入图片描述

3.2 用户相关数据加载

在这里插入图片描述

因为我们目前整套系统是对内提供登录访问,所以每次用户登录的时候oauth需要调用用户微服务查询用户信息,如上图:

我们需要在用户微服务中提供用户信息查询的方法,并在oauth中使用feign调用即可。

在真实工作中,用户和管理员对应的oauth认证服务器会分开,网关也会分开,我们今天的课堂案例只实现用户相关的认证即可。

1.Feign创建

在changgou-service-user-api中创建com.changgou.user.feign.UserFeign,代码如下:

/**
 * @Auther: csp1999
 * @Date: 2021/01/27/14:19
 * @Description: 用户微服务feign客户端接口
 */
@FeignClient(value = "changgou-user")
@RequestMapping(value = "/user")
public interface UserFeign {

    /***
     * 根据ID查询User数据
     * @param id
     * @return
     */
    @GetMapping({"/load/{id}"})
    //@GetMapping({"/{id}", "/load/{id}"})不能这样写!
    public Result<User> findById(@PathVariable String id);
}

2.修改UserController

修改changgou-service-user的UserController的findById方法,添加一个新的地址,用于加载用户信息,代码如下:

/***
 * 根据ID查询User数据
 * @param id
 * @return
 */
@GetMapping({"/{id}","/load/{id}"})
public Result<User> findById(@PathVariable String id) {
    // 调用UserService实现根据主键查询User
    User user = userService.findById(id);
    return new Result<User>(true, StatusCode.OK, "查询成功", user);
}

3.放行查询用户方法

因为oauth需要调用查询用户信息,需要在changgou-service-user中放行/user/load/{id}方法,修改ResourceServerConfig,添加对/user/load/{id}的放行操作,代码如下:

/**
 * Http安全配置,对每个到达系统的http请求链接进行校验
 *
 * @param http
 * @throws Exception
 */
@Override
public void configure(HttpSecurity http) throws Exception {
    // 所有请求必须认证通过
    http.authorizeRequests()
            // 下边的路径放行
            // 放行的请求路径不用登陆也可以访问
            .antMatchers(
                    "/user/add","/user/load/*"). // 配置地址放行
            permitAll()
            .anyRequest().
            authenticated();    // 其他地址需要认证授权
}

4.oauth调用查询用户信息

oauth认证微服务引入对changgou-user-api的依赖

<dependency>
    <groupId>com.changgou</groupId>
    <artifactId>changgou-service-user-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

修改oauth的com.changgou.oauth.config.UserDetailsServiceImplloadUserByUsername方法,调用UserFeign查询用户信息,代码如下:

//====================================用户信息认证 开始====================================
if (StringUtils.isEmpty(username)) {
    return null;
}
// 从数据库加载查询用户信息
Result<com.changgou.user.pojo.User> userResult = userFeign.findById(username);
// 判空
if (userResult == null || userResult.getData() == null) {
    return null;
}
// 根据用户名查询用户信息
//String pwd = new BCryptPasswordEncoder().encode("szitheima");
String pwd = userResult.getData().getPassword();
// 创建User对象
String permissions = "user,admin";// 指定用户的角色身份,普通用户user/admin用户
UserJwt userDetails = new UserJwt(username, pwd, AuthorityUtils.commaSeparatedStringToAuthorityList(permissions));
//====================================用户信息认证 结束====================================

完整的UserDetailsServiceImpl代码如下:

/**
 * 自定义授权认证类
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    ClientDetailsService clientDetailsService;

    @Autowired
    private UserFeign userFeign;

    /**
     * 自定义授权认证
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //====================================客户端信息认证 开始====================================
        // 取出身份,如果身份为空说明没有认证
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        // 没有认证统一采用httpbasic认证,httpbasic中存储了client_id和client_secret,开始认证client_id和client_secret
        if (authentication == null) {
            ClientDetails clientDetails = clientDetailsService.loadClientByClientId(username);
            if (clientDetails != null) {
                // 秘钥
                String clientSecret = clientDetails.getClientSecret();

                // 1.静态方式
                //return new User(
                //        username, // 客户端id
                //        new BCryptPasswordEncoder().encode(clientSecret), // 客户端密钥->加密操作
                //        AuthorityUtils.commaSeparatedStringToAuthorityList(""));// 权限

                // 2.数据库查找方式
                return new User(
                        username,// 客户端id
                        clientSecret,// 客户端密钥->不需要再加密操作,因为数据库中已经加密
                        AuthorityUtils.commaSeparatedStringToAuthorityList(""));// 权限
            }
        }
        //====================================客户端信息认证 结束====================================

        //====================================用户信息认证 开始====================================
        if (StringUtils.isEmpty(username)) {
            return null;
        }

        // 从数据库加载查询用户信息
        Result<com.changgou.user.pojo.User> userResult = userFeign.findById(username);

        // 判空
        if (userResult == null || userResult.getData() == null) {
            return null;
        }

        // 根据用户名查询用户信息
        //String pwd = new BCryptPasswordEncoder().encode("szitheima");
        String pwd = userResult.getData().getPassword();

        // 创建User对象
        String permissions = "user,admin";// 指定用户的角色身份,普通用户user/admin用户
        UserJwt userDetails = new UserJwt(username, pwd, AuthorityUtils.commaSeparatedStringToAuthorityList(permissions));
        //====================================用户信息认证 结束====================================

        //userDetails.setComy(songsi);
        return userDetails;
    }
}

5.主启动类Feign开启

/**
 * @Author: csp1999
 * @Date: 2020/7/6 8:01
 * @Description: OAUTH2 认证授权微服务启动类
 */
@SpringBootApplication
@EnableDiscoveryClient
//@MapperScan(basePackages = "com.changgou.auth.dao")
@EnableFeignClients("com.changgou.user.feign")
public class OAuthApplication {

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

    @Bean(name = "restTemplate")
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

6.测试

我们换个数据库中的账号密码登录,分别输入zhangsan,效果如下:

在这里插入图片描述