1.导入坐标

<!--导入spring cloud oauth2依赖 -->
        <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.11.RELEASE</version>
        </dependency>

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

        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <dependency>
            <!--mysql 数据库配置-->
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>

        <dependency>
            <!--数据库连接配置 -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!--MP代码自动生成-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.0</version>
        </dependency>

        <!-- 模板引擎 -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>

        <dependency>
            <!--mybatisplus-->
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.2.0</version>

        </dependency>

2.配置数据源

server:
  port: 999

spring:
  application:
    name: yyjzy-cloud-oauth-server
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.1.7:3306/oauth2?useUnicode=true&characterEncoding=utf8
    username: root
    password: 123456
    druid:
      initial-size: 10
      min-idle: 10
      max-active: 30
      max-wait: 50000

eureka:
  client:
    service-url:
      #注册到集群,多个用逗号隔开
      defaultZone: http://localhost:8761/eureka,http://localhost:8762/eureka
  instance:
    prefer-ip-address: true #服务实例中显示ip,而不是显示主机名(兼容老的eureka)
    # 实例名称: 192.168.1.6:lagou-cloud-oauth-server:999,我们可以自定义它
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml

3.token配置,接口权限配置

package com.yyj.edu.config;

import com.netflix.discovery.converters.Auto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.jwt.crypto.sign.MacSigner;
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.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.sql.DataSource;

@Configuration
@EnableAuthorizationServer  // 开启认证服务功能
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    AuthenticationManager authenticationManager;

    /**
     * 借口权限相关的配置
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);
        security
                .allowFormAuthenticationForClients() // 允许客户端的表单认证
                // 开启端口/oauth/token_key的访问权限
                .tokenKeyAccess("permitAll()")
                // 开启端口 /oauth/check_token的访问权限
                .checkTokenAccess("permitAll()");
    }

    /**
     * 客户端详情配置
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        super.configure(clients);
       // 从内存中加载客户端详情
//        clients.inMemory()// 存储方式 有内存   数据库等
//                // 添加客户端 制定client_id
//                .withClient("client_haha")
//                // 访问密码
//                .secret("yyjzy")
//                //  资源id 这里配置完还需要在 资源服务器配置
//                .resourceIds("code")
//                // 认证类型/令牌办法模式
//                .authorizedGrantTypes("password","refresh_token")
//                .scopes("all"); // 客户端的权限范围
        // 从数据库加载客户端详情
        clients.withClientDetails(createJdbcClientDetailsService());

    }

    @Autowired
    private DataSource dataSource;

    @Bean
    public JdbcClientDetailsService createJdbcClientDetailsService(){
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        return jdbcClientDetailsService;
    }

    /**
     * token相关的配置
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
        endpoints.tokenStore(tokenStore())  // 制定token的存储方式
                    .tokenServices(authorizationServerTokenServices()) // tokent的生成细节
                    .authenticationManager(authenticationManager) //制定认证管理器
                    .allowedTokenEndpointRequestMethods(HttpMethod.POST,HttpMethod.GET);
    }

    private String sign_key="haha123";

    /**
     * 设置token的存储方式
     * @return
     */
    public TokenStore tokenStore(){
        //return new InMemoryTokenStore();
        // 使用jwt令牌
        return  new JwtTokenStore(jwtAccessTokenConverter());
    }

    /**
     * 返回jwt令牌转换器
     * @return
     */
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(sign_key); // 签名秘钥
        jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key)); // 验证时使用的秘钥,和签名秘钥保持一致
        return  jwtAccessTokenConverter;


    }

    public AuthorizationServerTokenServices authorizationServerTokenServices(){
        // 使用默认实现
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setSupportRefreshToken(true); // 是否开启令牌刷新
        defaultTokenServices.setTokenStore(tokenStore());

        // 针对jwt令牌的添加
        defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());

        // 设置令牌有效时间
        defaultTokenServices.setAccessTokenValiditySeconds(80);
        // 设置刷新令牌的有效时间
        defaultTokenServices.setRefreshTokenValiditySeconds(258200);
        return defaultTokenServices;
    }
}

4.用户名 密码的校验

package com.yyj.edu.config;

import com.yyj.edu.service.impl.JdbcUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.ArrayList;

/**
 * 该配置类 主要配置用户名 密码的校验工作
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    JdbcUserDetailsService jdbcUserDetailsService;

    /**
     * 注册一个认证管理器对象
     * @return
     * @throws Exception
     */

    @Bean
    public AuthenticationManager authenticationManager()throws Exception{
        return super.authenticationManager();
    }

    /**
     * 密码编码对象
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    @Autowired
    PasswordEncoder passwordEncoder;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 从内存中获取用户名密码
//        UserDetails user = new User("admin","1234567",new ArrayList<>());
//        auth.inMemoryAuthentication()
//                .withUser(user).passwordEncoder(passwordEncoder);

        // 从数据库获取用户名密码
        auth.userDetailsService(jdbcUserDetailsService).passwordEncoder(passwordEncoder);
    }
}

5.userdetails封装 从数据库获取用户 密码

package com.yyj.edu.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yyj.edu.mapper.UsersMapper;
import com.yyj.edu.model.UsersDo;
import org.springframework.beans.factory.annotation.Autowired;
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.stereotype.Service;

import java.util.ArrayList;

@Service
public class JdbcUserDetailsService implements UserDetailsService {
    @Autowired
    UsersMapper usersMapper;
    /**
     *  根据username查询该用户所有信息,封装成userdetails类型对象返回
     * @param userName
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        QueryWrapper<UsersDo> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.eq("username",userName);

        UsersDo usersDo = usersMapper.selectOne(objectQueryWrapper);
        User user = new User(usersDo.getUsername(),usersDo.getPassword(),new ArrayList<>());
        return user;
    }
}

6.验证链接详解
1)生成token的链接

http://localhost:999/oauth/token?client_secret=yyjzy&grant_type=password&username=admin&password=123456&client_id=client_haha

client_secret: 设置的访问的密码
grant_type:认证类型
username:用户名
password:    访问密码
client_id: 客户端id

2) 检查token的链接

http://localhost:999/oauth/check_token?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzdW1lIiwiYXV0b2RlbGl2ZXIiXSwiZXhwIjoxNjEyNzkxNjU3LCJ1c2VyX25hbWUiOiJhZG1pbiIsImp0aSI6IjIyODg1YzQxLTFiNGMtNDA3NS05ZTQzLTUxZGI2NGI5YzYzNyIsImNsaWVudF9pZCI6ImNsaWVudF9sYWdvdTEyMyIsInNjb3BlIjpbImFsbCJdfQ.hv2jjvEgAVDpz-42xCUF5XZN1CXKXwolBRUOZkqLvSk

3)刷新token的链接

http://localhost:999/oauth/token?client_secret=yyjzy&grant_type=refresh_token&client_id=client_haha&refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzdW1lIiwiYXV0b2RlbGl2ZXIiXSwidXNlcl9uYW1lIjoiYWRtaW4iLCJzY29wZSI6WyJhbGwiXSwiYXRpIjoiMjI4ODVjNDEtMWI0Yy00MDc1LTllNDMtNTFkYjY0YjljNjM3IiwiZXhwIjoxNjEzMDQ5Nzc3LCJqdGkiOiI4OTE1MmM1Zi03MjRlLTRhZjEtYjBjNi03YTdmZjA2OTI0ZjciLCJjbGllbnRfaWQiOiJjbGllbnRfbGFnb3UxMjMifQ.CVbO9P4J34f-95FhFj6WnIgAaFnwQOlB-HnIE5QK9KQ

7.数据库的表
1)Oauth2客户端信息表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
 `client_id` varchar(48) NOT NULL,
 `resource_ids` varchar(256) DEFAULT NULL,
 `client_secret` varchar(256) DEFAULT NULL,
 `scope` varchar(256) DEFAULT NULL,
 `authorized_grant_types` varchar(256) DEFAULT NULL,
 `web_server_redirect_uri` varchar(256) DEFAULT NULL,
 `authorities` varchar(256) DEFAULT NULL,
 `access_token_validity` int(11) DEFAULT NULL,
 `refresh_token_validity` int(11) DEFAULT NULL,
 `additional_information` varchar(4096) DEFAULT NULL,
 `autoapprove` varchar(256) DEFAULT NULL,
 PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
BEGIN;
INSERT INTO `oauth_client_details` VALUES ('client_haha',
'autodeliver,resume', 'yyjzy', 'all', 'password,refresh_token',
NULL, NULL, 7200, 259200, NULL, NULL);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

2)验证⽤户合法性表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `username` char(10) DEFAULT NULL,
 `password` char(100) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of users
-- ----------------------------
BEGIN;
INSERT INTO `users` VALUES (4, 'admin', '123456');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

8.在资源服务器,也就是要访问认证服务的客户端添加配置

package com.yyj.edu.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.jwt.crypto.sign.MacSigner;
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.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
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;

@Configuration
@EnableResourceServer
@EnableWebSecurity
public class ResourceServerConfiger extends ResourceServerConfigurerAdapter {

    /**
     * 该方法用于定义资源服务器向远程认证服务器发起请求,进行token校验等事宜
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
//        // 设置当前资源服务的资源id
//        resources.resourceId("code");
//        // 定义token服务对象
//        RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
//        // 校验端点/接口设置
//        remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:999/oauth/check_token");
//        // 携带客户端id和安全码
//        remoteTokenServices.setClientId("client_lagou");
//        remoteTokenServices.setClientSecret("yyjzy");
//        resources.tokenServices(remoteTokenServices)

        // jwt令牌改造
        resources.resourceId("autodeliver").tokenStore(tokenStore()).stateless(true); // 无状态设置


    }

    /**
     * 设置白名单
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and()
                .authorizeRequests()
                .antMatchers("/yyjzy/**").authenticated()
                .anyRequest().permitAll(); // 其他请求不认证
    }

    private String sign_key="haha123";

    /**
     * 设置token的存储方式
     * @return
     */
    public TokenStore tokenStore(){
        //return new InMemoryTokenStore();
        // 使用jwt令牌
        return  new JwtTokenStore(jwtAccessTokenConverter());
    }

    /**
     * 返回jwt令牌转换器
     * @return
     */
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(sign_key); // 签名秘钥
        jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key)); // 验证时使用的秘钥,和签名秘钥保持一致
        return  jwtAccessTokenConverter;


    }
}