Spring cloud OAuth2

1、OAuth2.0简介

OAuth(开发授权)是一个开放标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方应用或分享他们数据的所有内容。

OAuth2.0是OAuth的延续,但他并不兼容OAuth1.0,即它完全废除OAuth1.0。很多大公司如Google,Yahoo,Microsoft等都提供了OAuth认证服 务,这些都足以说明OAUTH标准逐渐成为开放资源授权的标准。

微信小程序授权登录同样也是提供OAuth认证服务

2、业务场景

第三方认证

当需要访问第三方系统的资源时需要首先通过第三方系统的认证(例如:微信认证),由第三方系统对用户认证通过,并授权资源的访问权限。

你恰好之前在我们的B网站上注册过账号,那么现在就可以使用B网站上的账号登录在CSDN上。那么我们在B网站上做这样的一个功能就是使用OAuth2做的。

OAuth2.0包含一下几种角色

1,客户端:本身不存储资源,需要通过资源拥有者的授权去请求资源服务器的资源。

2,资源拥有者:通常为用户,也可以是应用程序,即该资源的拥有者。

3,授权服务器(认证服务器):用于服务提供商对资源拥有者的身份认证,对访问资源进行授权,认证成功后会给客户端发放令牌(access_token),作为客户端访问资源服务器的凭据。

4,资源服务器:存储资源的服务器

5,客户端标识:client_id

6,客户端密钥:client_secret

为什么要有client_id和client_secret呢?

因为服务提供商不能允许随便一个客户端就接入到它的授权服务器,服务提供商会 给准入的接入方一个身份,用于接入时的凭据。

3,环境搭建

  1. 项目工程

shop-oauth-service 认证服务(颁发Token)

shop-gateway-service 网关(认证用户的请求 也即认证Token)

shop-service 服务(资源服务)

  1. 项目依赖pom.xml文件

1.父模块pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
    </parent>
    <groupId>com.lpz</groupId>
    <artifactId>shop-parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>shop-server</module>
        <module>shop-api</module>
        <module>shop-gateway-server</module>
        <module>shop-oauth-server</module>

    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

2.网关模块pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>shop-parent</artifactId>
        <groupId>com.lpz</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shop-gateway-server</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <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>
        </dependency>
    </dependencies>
</project>

3.shop-server模块pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>shop-parent</artifactId>
        <groupId>com.lpz</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shop-server</artifactId>
    <packaging>pom</packaging>
    <modules>
        <module>shop-user-server</module>
        <module>shop-goods-server</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--        服务注册/发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

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

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

4.shop-oauth-server模块pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>shop-parent</artifactId>
        <groupId>com.lpz</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shop-oauth-server</artifactId>

    <dependencies>
        <!--        服务注册/发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-data</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.security.oauth.boot</groupId>
                    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>


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

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

        <dependency>
            <groupId>com.lpz</groupId>
            <artifactId>shop-user-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

</project>

shop-oauth-server yml配置文件

server:
  port: 1100
spring:
  application:
    name: user-auth
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/shop_oauth?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=UTC
    username: root
    password: abc123
  main:
    allow-bean-definition-overriding: true
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.56.1:8848
auth:
  ttl: 3600  #token存储到redis的过期时间
  clientId: accp
  clientSecret: accp
  cookieDomain: localhost
  cookieMaxAge: -1
encrypt:
  key-store:
    location: classpath:/wfshop.jks
    secret: wfshop
    alias: wfshop
    password: wfshop

AuthorizationServerConfig配置类

package com.lpz.oauth.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.security.KeyPair;


@Configuration
@EnableAuthorizationServer
class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    //数据源,用于从数据库获取数据进行认证操作,测试可以从内存中获取
    @Autowired
    private DataSource dataSource;
    //jwt令牌转换器
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    //SpringSecurity 用户自定义授权认证类
    @Autowired
    UserDetailsService userDetailsService;
    //授权认证管理器
    @Autowired
    AuthenticationManager authenticationManager;
    //令牌持久化存储接口
    @Autowired
    TokenStore tokenStore;
    @Autowired
    private CustomUserAuthenticationConverter customUserAuthenticationConverter;

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

    /***
     * 授权服务器端点配置
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.accessTokenConverter(jwtAccessTokenConverter)
                .authenticationManager(authenticationManager)//认证管理器
                .tokenStore(tokenStore)                       //令牌存储
                .userDetailsService(userDetailsService);     //用户信息service
    }

    /***
     * 授权服务器的安全配置
     * @param oauthServer
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.allowFormAuthenticationForClients()
                .passwordEncoder(new BCryptPasswordEncoder())
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()");
    }


    //读取密钥的配置
    @Bean("keyProp")
    public KeyProperties keyProperties(){
        return new KeyProperties();
    }

    @Resource(name = "keyProp")
    private KeyProperties keyProperties;

    //客户端配置
    @Bean
    public ClientDetailsService clientDetails() {
        return new JdbcClientDetailsService(dataSource);
    }

    @Bean
    @Autowired
    public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
        return new JwtTokenStore(jwtAccessTokenConverter);
    }

    /****
     * JWT令牌转换器
     * @param customUserAuthenticationConverter
     * @return
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(CustomUserAuthenticationConverter customUserAuthenticationConverter) {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        KeyPair keyPair = new KeyStoreKeyFactory(
                keyProperties.getKeyStore().getLocation(),                          //证书路径 changgou.jks
                keyProperties.getKeyStore().getSecret().toCharArray())              //证书秘钥 changgouapp
                .getKeyPair(
                        keyProperties.getKeyStore().getAlias(),                     //证书别名 changgou
                        keyProperties.getKeyStore().getPassword().toCharArray());   //证书密码 changgou
        converter.setKeyPair(keyPair);
        //配置自定义的CustomUserAuthenticationConverter
        DefaultAccessTokenConverter accessTokenConverter = (DefaultAccessTokenConverter) converter.getAccessTokenConverter();
        accessTokenConverter.setUserTokenConverter(customUserAuthenticationConverter);
        return converter;
    }
}

CustomUserAuthenticationConverter类

package com.lpz.oauth.config;

import com.lpz.oauth.util.UserJwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
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.oauth2.provider.token.DefaultUserAuthenticationConverter;
import org.springframework.stereotype.Component;

import java.util.LinkedHashMap;
import java.util.Map;

@Component
public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter {

    @Autowired
    UserDetailsService userDetailsService;

    @Override
    public Map<String, ?> convertUserAuthentication(Authentication authentication) {
        LinkedHashMap response = new LinkedHashMap();
        String name = authentication.getName();
        response.put("username", name);

        Object principal = authentication.getPrincipal();
        UserJwt userJwt = null;
        if(principal instanceof  UserJwt){
            userJwt = (UserJwt) principal;
        }else{
            //refresh_token默认不去调用userdetailService获取用户信息,这里我们手动去调用,得到 UserJwt
            UserDetails userDetails = userDetailsService.loadUserByUsername(name);
            userJwt = (UserJwt) userDetails;
        }
        response.put("name", userJwt.getName());
        response.put("id", userJwt.getId());
        //公司 response.put("compy", "songsi");
        if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
            response.put("authorities", AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
        }
        return response;
    }

}

UserDetailsServiceImpl实现类

package com.lpz.oauth.config;

import com.lpz.dto.TbUserDto;
import com.lpz.feign.UserFeignService;
import com.lpz.oauth.util.UserJwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

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

    @Autowired
    ClientDetailsService clientDetailsService;
    @Autowired
    UserFeignService userFeignService;

    /****
     * 自定义授权认证
     * @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();
                //静态方式
                // return new User(username,new BCryptPasswordEncoder().encode(clientSecret), AuthorityUtils.commaSeparatedStringToAuthorityList(""));
                //数据库查找方式
                return new User(username, clientSecret, AuthorityUtils.commaSeparatedStringToAuthorityList(""));
            }
        }

        if (StringUtils.isEmpty(username)) {
            return null;
        }

        //根据用户名查询用户信息
        TbUserDto info = userFeignService.info(username);
        String pwd = info.getPassword();
        //创建User对象
        String permissions = "goods_list,seckill_list";


        UserJwt userDetails = new UserJwt(username, pwd, AuthorityUtils.commaSeparatedStringToAuthorityList(permissions));


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

WebSecurityConfig配置类

package com.lpz.oauth.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
@Order(-1)
class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /***
     * 忽略安全拦截的URL
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                "/oauth/login",
                "/user/logout");
    }

    /***
     * 创建授权管理认证对象
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        AuthenticationManager manager = super.authenticationManagerBean();
        return manager;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }

    /***
     * 采用BCryptPasswordEncoder对密码进行编码
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /****
     *
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .httpBasic()        //启用Http基本身份验证
                .and()
                .formLogin()       //启用表单身份验证
                .and()
                .authorizeRequests()    //限制基于Request请求访问
                .anyRequest()
                .authenticated();       //其他请求都需要经过验证

    }
}

AuthToken工具类

package com.lpz.oauth.util;

import java.io.Serializable;


public class AuthToken implements Serializable{

    //令牌信息
    String accessToken;
    //刷新token(refresh_token)
    String refreshToken;
    //jwt短令牌
    String jti;

    public String getAccessToken() {
        return accessToken;
    }

    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }

    public String getRefreshToken() {
        return refreshToken;
    }

    public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
    }

    public String getJti() {
        return jti;
    }

    public void setJti(String jti) {
        this.jti = jti;
    }
}

UserJwt工具类

package com.lpz.oauth.util;


import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import java.util.Collection;


public class UserJwt extends User {
    private String id;    //用户ID
    private String name;  //用户名字

    private String comny;//设置公司

    public UserJwt(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

ResourceServerConfig(资源服务器上的配置类)

package com.lpz.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.stream.Collectors;

@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(
                        "/tbuser/info/**"). //配置地址放行
                permitAll()
                .anyRequest().
                authenticated();    //其他地址需要认证授权
    }
}

gateway-server(全局过滤器代码)

package com.lpz.filters;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
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.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class PowerFilters implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String path = request.getURI().getPath();
        if (path.startsWith("/api/oauth/login")) {
            return chain.filter(exchange);
        }
        String token = request.getQueryParams().getFirst("token");
        if (StringUtils.isEmpty(token)) {
            token = request.getHeaders().getFirst("token");
            if (StringUtils.isEmpty(token)) {
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
        }
        request.mutate().header("Authorization", "bearer " + token);
        return chain.filter(exchange);
    }

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

shop-oauth-server(AuthServiceImpl代码)

package com.lpz.oauth.service.impl;

import com.lpz.oauth.service.AuthService;
import com.lpz.oauth.util.AuthToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.Base64;
import java.util.List;
import java.util.Map;
@Service
public class AuthServiceImpl implements AuthService {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private DiscoveryClient discoveryClient;

    @Override
    public AuthToken login(String username, String password, String clientId, String clientSecret) {
        List<ServiceInstance> instances = discoveryClient.getInstances("oauth-server");
        ServiceInstance serviceInstance = instances.get(0);
        String url = "http://" + serviceInstance.getHost()+":" + serviceInstance.getPort() + "/oauth/token";
        LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "password");
        body.add("username", username);
        body.add("password", password);
        LinkedMultiValueMap<String, String> head = new LinkedMultiValueMap<>();
        head.add("Authorization", myEncoder(clientId, clientSecret));
        ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(body, head), Map.class);
        Map map = response.getBody();
        System.out.println(map);
        AuthToken authToken = new AuthToken();
        authToken.setAccessToken(map.get("access_token").toString());
        authToken.setJti(map.get("jti").toString());
        authToken.setRefreshToken(map.get("refresh_token").toString());
        return authToken;
    }

    private static String myEncoder(String clientId, String clientSecret) {
        String code = clientId + ":" + clientId;
        byte[] encode = Base64Utils.encode(code.getBytes());
        return "Basic " + new String(encode);

    }

    public static void main(String[] args) {
        String s = myEncoder("changgou", "changgou");
        System.out.println(s);
    }
}

4.资源服务授权流程

  1. 钥私钥授权流程

上图的业务流程如下:

客户端请求认证服务申请令牌
认证服务生成令牌认证服务采用非对称加密算法,使用私钥生成令牌。
客户端携带令牌访问资源服务客户端在Http header 中添加: Authorization:Bearer 令牌。
资源服务请求认证服务校验令牌的有效性资源服务接收到令牌,使用公钥校验令牌的合法性。
令牌有效,资源服务向客户端响应资源信息
2.公钥私钥

在对称加密的时代,加密和解密用的是同一个密钥,这个密钥既用于加密,又用于解密。这样做有一个明显的缺点,如果两个人之间传输文件,两个人都要知道密钥,如果是三个人呢,五个人呢?于是就产生了非对称加密,用一个密钥进行加密(公钥),用另一个密钥进行解密(私钥)。

总结:公钥加密、私钥解密、私钥签名、公钥验签。

3. 生成私钥公钥

Spring Security 提供对JWT的支持,本节我们使用Spring Security 提供的JwtHelper来创建JWT令牌,校验JWT令牌 等操作。 这里JWT令牌我们采用非对称算法进行加密,所以我们要先生成公钥和私钥。

(1)生成密钥证书 下边命令生成密钥证书,采用RSA 算法每个证书包含公钥和私钥

创建一个文件夹,在该文件夹下执行如下命令行:

keytool -genkeypair -alias qianggou -keyalg RSA -keypass qianggou -keystore qianggou.jks -storepass qianggou     

Keytool 是一个java提供的证书管理工具

-alias:密钥的别名 
-keyalg:使用的hash算法 
-keypass:密钥的访问密码 
-keystore:密钥库文件名,qianggou.keystore保存了生成的证书 
-storepass:密钥库的访问密码 

查询证书信息

keytool -list -keystore qianggou.jks

删除别名

keytool -delete -alias qianggou -keystore qianggou.jsk
4.导出公钥

openssl是一个加解密工具包,这里使用openssl来导出公钥信息。

安装 openssl:http://slproweb.com/products/Win32OpenSSL.html

安装资料目录下的Win64OpenSSL-1_1_0g.exe

配置openssl的path环境变量,如下图:

本教程配置在C:\OpenSSL-Win64\bin

cmd进入qianggou.jks文件所在目录执行如下命令(如下命令在windows下执行,会把-变成中文方式,请将它改成英文的-):

keytool -list -rfc --keystore qianggou.jks | openssl x509 -inform pem -pubkey

下面段内容是公钥

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhTN8iyCyQEZPUmLw0GB0
rH0xeT23w4c8X8thfEtphIBTAKOoUKHxhzBHra/WHLpPw/fV/m9KTvbWCdlBziKW
r4P//KtUoa5D3qJupsPDxl9GTXJjG/9y/E+9hHLtl+cM2Qu+RTayFQ4X4ZqLjiOc
lZZjN8g3MUomu45ab7PVJvuNUGcMq8s+pSvIwHQejYVKlPH8amSqyyxEyzH2MFBX
N1c0KoerMfaDV0uzFCsUdOxUlpow9byCPZAspuNbN3DG3xwoHtOErWcchs8TlGMy
So2QAeGADmCkH/iIHbF22ytmybIsbK5Ww9T+a1xK+wzzKBAucL+HuK5hLwQlSyuq
zQIDAQAB
-----END PUBLIC KEY-----

将上边的公钥拷贝到文本public.key文件中,合并为一行,可以将它放到需要实现授权认证的工程中。

5.运行图

在运行之前我们再来梳理一下流程

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: SpringCloud Oauth2 是一种基于OAuth2的开放授权框架,可以帮助开发者快速构建安全的RESTful API。 springcloud oauth2 是一种开放授权标准,它可以帮助开发人员更容易地为应用程序实现安全的身份验证和授权。好的,下面是关于 Spring CloudOAuth2 的一些解释和概述。 Spring Cloud 是一个基于 Spring 框架的开发工具集,它可以帮助开发者快速构建分布式系统和微服务架构。Spring Cloud 提供了多种功能模块,包括服务发现、配置管理、负载均衡、断路器等等,以便于开发者构建和管理微服务架构。 OAuth2 是一种授权框架,可以用于保护 API、Web 应用程序或其他服务的访问权限。OAuth2 通过颁发访问令牌(Access Token)来实现授权,访问令牌是在用户授权的情况下,由授权服务器颁发给客户端的。 Spring Cloud 提供了一个名为 Spring Cloud Security 的安全框架,它可以集成 OAuth2,以实现安全访问控制和身份验证。通过 Spring Cloud Security 和 OAuth2 的集成,开发者可以构建安全的微服务架构,以保护系统和数据的安全性。 在 Spring Cloud 中,集成 OAuth2 的方式有很多种,比如使用 Spring Security OAuth2 或者使用 Spring Cloud Security OAuth2,开发者可以根据自己的需求和技术栈选择适合自己的方案。 ### 回答2: Spring Cloud OAuth2 是Spring Cloud 微服务框架中的一种安全认证授权的解决方案。它可以为我们提供 OAuth2 认证和授权服务,帮助我们实现微服务架构中的单点登录、安全控制等功能。 OAuth2 具有很高的灵活性和可定制性,可以实现多种认证方式和授权模式,例如授权码模式、密码模式、客户端模式等。Spring Cloud OAuth2 基于 Spring Security 实现了 OAuth2 协议,并且采用了简化的配置方式和自动化的注册中心,简化了我们的认证授权开发。 在 Spring Cloud OAuth2 中,应用程序被分为两类:认证服务器和资源服务器。认证服务器主要负责处理登录请求和颁发访问令牌,而资源服务器则根据令牌来检查并限制对受保护资源的访问。 除此之外,Spring Cloud OAuth2 还提供了一些附加功能,如 Spring Cloud Security,它是 Spring Security 的增强版,提供许多安全功能和 OAuth2 支持,可以帮助我们轻松地实现授权码、密码和客户端等多种认证模式。 总之,Spring Cloud OAuth2 可以作为我们微服务架构中的认证和授权解决方案,帮助我们实现单点登录、安全控制等功能。通过该解决方案,我们可以为不同的服务提供不同的授权策略和安全机制,提高微服务架构的稳定性和安全性。 ### 回答3: SpringCloud OAuth2是基于Spring CloudSpring Security实现的一种开源的身份验证和授权协议。它主要用于实现用户认证和授权功能,并提供了一些常见的认证协议和授权机制,例如OAuth2、OpenID Connect、JWT等。通过使用SpringCloud OAuth2,开发人员可以以一种简单、可扩展的方式为应用程序添加安全性,同时也可以更好地控制资源的访问。 SpringCloud OAuth2的核心是通信协议和两种角色(客户端和资源服务器)。其中,客户端是指请求API资源的应用程序,而资源服务器则负责保护和提供这些API资源。为了实现安全机制,SpringCloud OAuth2引入了许多重要的概念: 1、认证服务器:用于验证用户身份的服务器,通知客户端提供的令牌是否有效。 2、客户端:请求受保护的资源的应用程序。 3、资源服务器:保护和提供受保护资源的服务器。 4、访问令牌:许可授予客户端访问资源的票据。 5、刷新令牌:用于刷新和获取新的访问令牌的令牌。 6、授权代码:通过重定向浏览器并向客户端授权服务器发送欲请求访问的资源,以获取访问和刷新令牌。 7、JWT:JSON Web Tokens,一种安全地传输信息并可验证的JSON标准。 总之,SpringCloud OAuth2是一项非常有用的技术,可帮助开发人员实现应用程序的身份验证和授权。它提供了一些常见的认证协议和授权机制,例如OAuth2、OpenID Connect、JWT等,可以让开发人员更好地控制资源的访问。同时它还使用了一系列的重要概念和角色,如认证服务器、客户端、资源服务器、访问令牌、刷新令牌、授权代码、JWT等,为应用程序添加安全性和可扩展性提供了有力的保障。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值