likes
comments
collection
share

Spring Security实现OAuth2协议及实战

作者站长头像
站长
· 阅读数 21

Spring Security实现OAuth2协议及实战

文章篇幅较长,愿读者耐心看完。如有不足之处,请指正。

一.OAuth2介绍

1.1 OAuth2是什么 怎么用

OAuth2是目前最流行的授权协议,用来授权第三方应用,获取用户数据。 举个例子:快递员想要进入小区,有3种方式。1是业主远程开门,2是业主告诉门禁密码,3是使用令牌(Oauth2)。 如图: Spring Security实现OAuth2协议及实战 令牌和密码的区别:令牌相当于火车票,密码相当于是钥匙。

  • 令牌是短期的,自动失效。密码是长期有效。
  • 令牌是可以撤销的,撤销立即生效。密码一般不允许他们撤销。
  • 令牌有权限范围,如车票座位为10车A15座。密码一般是完整权限。

第三方登录演示(网易云客户端利用QQ扫码登录) Spring Security实现OAuth2协议及实战 网易云客使用QQ扫码登录中Oauth2协议各个角色扮演者

  • Rrsource Owner: 用户
  • Client: 网易云
  • Authorization Server: QQ
  • Resource Server: QQ
  • User Agent: 浏览器

Oauth2.0与1.0的区别:

  • OAuth2.0有4种授权模式。优缺点:1.去掉签名,改用SSL(HTTPS)确保安全性。2.所有的token不再有对应的secret存在,签名过程简洁,这也直接导致OAuth2.0不能兼容老版本。3.能更好的支持不是基于浏览器的应用。4.OAuth2.0的访问令牌是"短命的",且可以刷新令牌。
  • OAuth1.0只有一种授权模式。优缺点:用的是http协议,申请RequestToken过程中,容易把攻击者调包。2.攻击者调包后的目的是为了仿造回调地址,拿到用户的accessToken。

Oauth2授权模式:

  • 授权码模式:最完整和严谨的授权模式,第三方平台登录都是该模式。安全性最高。
  • 简化模式:省略授权码阶段,客户端是纯静态页面采用该模式。安全性高。
  • 密码模式:把用户名密码告诉客户端,对客户端高度信任,比如客户端和认证服务器是同一公司。安全性一般。
  • 客户端模式:直接因客户端名义申请令牌,很少有。安全性最差。
1.2 为什么要用OAuth2

Spring Security实现OAuth2协议及实战

  • cookie是不能跨域的,前后端分离分布式架构实现多系统SSO非常困难。
  • 移动端应用没有cookie,所以对于移动端支持不友好。
  • token基于header传递,部分解决了CSRF攻击。
  • token比sessionID大,客户端存储在Local Storage中,可以直接被JS读取。

二.OAuth2实战

2.1 搭建项目

首先创建两个项目 一个是授权项目 一个是资源项目 授权项目目录(java-skill-point) Spring Security实现OAuth2协议及实战

pom.xml文件 这里要注意springboot与springcloud的版本号

<?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 https://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.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.weige</groupId>
    <artifactId>java-skill-point</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>java-skill-point</name>
    <description>java-skill-point</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR5</spring-cloud.version>
    </properties>
    <dependencies>
        <!--Springboot项目自带 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--Springboot Web项目 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>

        <!-- hutool  -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.20</version>
        </dependency>

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

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

    </dependencies>

    <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>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml文件

server:
  port: 8888

OAuth2Config文件

package com.weige.javaskillpoint.config;

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.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.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;

    private static final String CLIENT_ID = "cms";
    private static final String SECRET_CHAR_SEQUENCE = "{noop}secret";
    private static final String ALL = "all";
    private static final int ACCESS_TOKEN_VALIDITY_SECONDS = 30 * 60;
    // 密码模式授权模式
    private static final String GRANT_TYPE_PASSWORD = "password";
    //授权码模式
    private static final String AUTHORIZATION_CODE = "authorization_code";
    //简化授权模式
    private static final String IMPLICIT = "implicit";
    //客户端模式
    private static final String CLIENT_CREDENTIALS = "client_credentials";

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
                .inMemory()
                .withClient(CLIENT_ID)
                .secret(SECRET_CHAR_SEQUENCE)
                .autoApprove(false)
                .redirectUris("http://127.0.0.1:8084/cms/login") //重定向uri
                .scopes(ALL)
                .accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
                .authorizedGrantTypes(AUTHORIZATION_CODE, IMPLICIT, GRANT_TYPE_PASSWORD, CLIENT_CREDENTIALS);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager).tokenStore(memoryTokenStore());
    }

    /**
     * 认证服务器的安全配置
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //  开启/oauth/check_token验证端口认证权限访问,checkTokenAccess("isAuthenticated()")设置授权访问
                .checkTokenAccess("permitAll()")
                //允许表单认证
                .allowFormAuthenticationForClients();
    }

    @Bean
    public TokenStore memoryTokenStore() {
        return new InMemoryTokenStore();
    }

}

SecurityConfig文件

package com.weige.javaskillpoint.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;

@Configuration
@EnableWebSecurity
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {    //auth.inMemoryAuthentication()
        auth.inMemoryAuthentication()
                .withUser("lxs")
                .password("{noop}123") //使用springsecurity5,需要加上{noop}指定使用NoOpPasswordEncoder给DelegatingPasswordEncoder去校验密码
                .roles("admin");
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //解决静态资源被拦截的问题
//        web.ignoring().antMatchers("/asserts/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .formLogin().permitAll()
                .and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
                .and().authorizeRequests().antMatchers("/oauth/**", "/login/**", "/logout/**", "/api/**").permitAll()
                .anyRequest().authenticated()
                // 关闭跨域保护;
                .and().csrf().disable();
    }

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

}

授权认证项目代码码完了 接下来给大家介绍代码的含义 重点是OAuth2Config与SecurityConfig两个类 OAuth2Config代码介绍: Spring Security实现OAuth2协议及实战 SecurityConfig代码介绍: Spring Security实现OAuth2协议及实战 资源项目目录(oauth2-service-01) Spring Security实现OAuth2协议及实战 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 https://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.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.weige</groupId>
    <artifactId>java-skill-point</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>java-skill-point</name>
    <description>java-skill-point</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR5</spring-cloud.version>
    </properties>
    <dependencies>
        <!--Springboot项目自带 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--Springboot Web项目 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>


        <!-- hutool  -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.20</version>
        </dependency>

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

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

    </dependencies>

    <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>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml文件

server:
  port: 8084

Oauth2ResourceServerConfiguration文件

package com.weige.javaskillpoint.config;

import java.io.IOException;
import javax.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
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.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;


@Configuration
@EnableResourceServer
public class Oauth2ResourceServerConfiguration extends
    ResourceServerConfigurerAdapter {

  private static final String CHECK_TOKEN_URL = "http://localhost:8888/oauth/check_token";

  @Override
  public void configure(ResourceServerSecurityConfigurer resources) {

    RemoteTokenServices tokenService = new RemoteTokenServices();

    tokenService.setCheckTokenEndpointUrl(CHECK_TOKEN_URL);
    tokenService.setClientId("cms");
    tokenService.setClientSecret("secret");

    resources.tokenServices(tokenService);
  }

}

SecurityConfiguration文件

package com.weige.javaskillpoint.config;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().antMatchers("/**").authenticated();
    // 禁用CSRF
    http.csrf().disable();
  }
}

HelloController文件

package com.weige.javaskillpoint.controller;

import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;

@RestController
@RequestMapping("/cms")
public class HelloController {

    @GetMapping("/getCurrentUser")
    public Object getCurrentUser(Authentication authentication) {
        return authentication;
    }

    @GetMapping("/index")
    public String index() {
        return "index";
    }


    @PutMapping("/index2")
    public String index2() {
        return "index2";
    }

}

资源项目代码码完了 接下来给大家介绍代码的含义 重点是Oauth2ResourceServerConfiguration与SecurityConfiguration两个类 Oauth2ResourceServerConfiguration代码介绍: Spring Security实现OAuth2协议及实战 SecurityConfiguration代码介绍: Spring Security实现OAuth2协议及实战 HelloController类中定义了受限访问的资源

2.2 启动两个项目 并进行接口测试

启动授权服务项目,结果演示: 测试: 随便校验一个令牌:校验接口 http://localhost:8888/oauth/check_token?token=abcdefg123456 Spring Security实现OAuth2协议及实战启动资源服务项目,访问接口: 没有携带token,访问无权限 Spring Security实现OAuth2协议及实战

2.3 四种授权模式接口演示

授权码模式: Spring Security实现OAuth2协议及实战 1.申请授权码 localhost:8888/oauth/authorize?client_id=cms&cliect_secret=secret&response_type=code

  • client_id:客户端id,和授权配置类中设置的客户端id一致。
  • response_type:授权码模为code。
  • cliect_secret:客户端密码,和授权配置类中设置的secret一致。

Spring Security实现OAuth2协议及实战 Spring Security实现OAuth2协议及实战 Spring Security实现OAuth2协议及实战

Spring Security实现OAuth2协议及实战 Spring Security实现OAuth2协议及实战

2.根据授权码申请令牌(在PostMan中调用接口):http://localhost:8888/oauth/token?code=ckujfQ&grant_type=authorization_code&redirect_url=http://127.0.0.1:8084/cms/login&scope=all

  • grant_type:授权类型,填写authorization_code,表示授权码模式。
  • code:授权码,注意授权码使用一次就无效了,需要重新申请。
  • redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数,这里需要。 Spring Security实现OAuth2协议及实战 Spring Security实现OAuth2协议及实战 令牌校验:http://localhost:8888/oauth/check_token?token=4a7e8dbc-24ef-4517-8de3-d9b0229dfa0b Spring Security实现OAuth2协议及实战 使用令牌:http://localhost:8888/oauth/check_token?token=4a7e8dbc-24ef-4517-8de3-d9b0229dfa0b 携带token则访问资源项目接口成功 Spring Security实现OAuth2协议及实战 简化模式: Spring Security实现OAuth2协议及实战

申请令牌 浏览器请求:localhost:8888/oauth/authorize?client_id=cms&redirect_uri=http://127.0.0.1:8084/cms/login&response_type=token&scope=all

Spring Security实现OAuth2协议及实战 输入网址回车 Spring Security实现OAuth2协议及实战Spring Security实现OAuth2协议及实战 Spring Security实现OAuth2协议及实战校验令牌:http://localhost:8888/oauth/check_token?token=f9013048-946b-4875-a83a-69371e7e0829 Spring Security实现OAuth2协议及实战 资源测试 Spring Security实现OAuth2协议及实战 密码模式: Spring Security实现OAuth2协议及实战 申请令牌 使用PostMan:localhost:8888/oauth/token?grant_type=password&username=lxs&password=123&scope=all

Spring Security实现OAuth2协议及实战 Spring Security实现OAuth2协议及实战 校验令牌:http://localhost:8888/oauth/check_token?token=1365345d-8765-47d4-8d2f-563a31a8b3a7 Spring Security实现OAuth2协议及实战 资源测试 Spring Security实现OAuth2协议及实战 客户端模式: Spring Security实现OAuth2协议及实战 申请令牌 使用PostMan:localhost:8888/oauth/token?grant_type=password&username=lxs&password=123&scope=all Spring Security实现OAuth2协议及实战 校验令牌:http://localhost:8888/oauth/check_token?token=5c58a1ef-e1d5-49e7-9ef7-da1063261998 Spring Security实现OAuth2协议及实战 资源测试 Spring Security实现OAuth2协议及实战

三.Spring Security实现OAuth2协议实战(对标大厂)

3.1 生成JWT公钥私钥

1. ⽣成密钥证书 下边命令⽣成密钥证书,采⽤RSA 算法每个证书包含公钥和私钥 创建⼀个⽂件夹,在该⽂件夹下执⾏如下命令⾏:

keytool -genkeypair -alias kaikeba -keyalg RSA -keypass kaikeba -keystore kaikeba.jks -storepass kaikeb

参数解释:

  • alias:密钥的别名
  • keyalg:使⽤的hash算法
  • keypass:密钥的访问密码
  • keystore:密钥库⽂件名
  • storepass:密钥库的访问密码

Spring Security实现OAuth2协议及实战 导出公钥 下载网址 https://slproweb.com/products/Win32OpenSSL.html Spring Security实现OAuth2协议及实战 配置环境变量 Spring Security实现OAuth2协议及实战 cmd进⼊kaikeba.jks⽂件所在⽬录执⾏如下命令 keytool -list -rfc --keystore kaikeba.jks | openssl x509 -inform pem -pubkey Spring Security实现OAuth2协议及实战 下⾯段内容是公钥 -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAojlMgqf4RVL0ExMpJWoO q35eow0lhDFTpUOUkU1ZGWoK+ZwQFUnR5w5U+u2mHqq3dFJWUIAmQC+y5p2ClsvG TbAvYQmL1k4X6oaGj7Bi9SSX6QQeM5bXsrXsjsGJyQyIbqgyBYIg4ZNB29UDcTCv xdFl8+rXoOppqENnTZpij8EIzJooCfrc2GzAeljmgPi4DFJnDAxE4joVz70xWk36 noRHSEfvmUaW+1S1T0cEH+j9p8PUGonnjqU8R6ZPmKAhU1w2t002dLqtkFDzxPW7 M1uJ4hq/CL7smvkOkGb0UVLAwFR9hzO2loxn/y0DcRdyxb5FVPQXnjXCp2hyT0mX pwIDAQAB -----END PUBLIC KEY-----

创建public.key文件 将公钥复制进去 Spring Security实现OAuth2协议及实战

3.2 搭建授权项目与资源项目

搭建授权项目 项目目录

Spring Security实现OAuth2协议及实战

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 https://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.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.weige</groupId>
    <artifactId>java-skill-point</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>java-skill-point</name>
    <description>java-skill-point</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR5</spring-cloud.version>
    </properties>
    <dependencies>
        <!--Springboot项目自带 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--Springboot Web项目 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>

        <!--Mysql依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- mybatis 用于接口注解快速开发(@Insert @Select @Update) -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <!--用于扫描mybatis注解 @Mapper @MapperScan-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <!-- hutool  -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.20</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>

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

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

    </dependencies>

    <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>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

server:
  port: 8888

spring:
  main:
    allow-bean-definition-overriding: true
  # mysql连接信息
  datasource:
    # mysql8之后
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://43.143.132.109:3306/index?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
    username: root
    password: ******

大家在这里要创建对应的数据库index 并创建oauth_client_details表

CREATE TABLE `oauth_client_details` (
  `client_id` varchar(256) COLLATE utf8mb4_german2_ci NOT NULL,
  `resource_ids` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
  `client_secret` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
  `scope` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
  `authorized_grant_types` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
  `web_server_redirect_uri` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
  `authorities` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
  `access_token_validity` int(11) DEFAULT NULL,
  `refresh_token_validity` int(11) DEFAULT NULL,
  `additional_information` varchar(4096) COLLATE utf8mb4_german2_ci DEFAULT NULL,
  `autoapprove` varchar(256) COLLATE utf8mb4_german2_ci DEFAULT NULL,
  PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_german2_ci;

INSERT INTO `index`.`oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('cms', '', '$2a$10$WZQaLHfS6amrJzN50wE3e.upn8KIi1wmCH9FSdZE6OBt8OKSyGLm.', 'read, write', 'client_credentials,implicit,authorization_code,refresh_token,password', 'http://www.baidu.com', NULL, NULL, NULL, NULL, 'false');

## '$2a$10$WZQaLHfS6amrJzN50wE3e.upn8KIi1wmCH9FSdZE6OBt8OKSyGLm.' 是加密过后的123456

AdminTokenConstant文件

package com.weige.javaskillpoint.config;

public class AdminTokenConstant {

    /**
     * 秘钥全名称
     */
    public static final String KEY_LOCATION = "kaikeba.jks";

    /**
     * 密钥的密码,此密码和别名要匹配
     */
    public static final String KEY_PASSWORD = "kaikeba";
}

AuthorizationServerConfiguration文件

package com.weige.javaskillpoint.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
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.DefaultTokenServices;
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.util.concurrent.TimeUnit;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private DataSource dataSource;

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

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        // 证书路径和密钥库密码
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource(AdminTokenConstant.KEY_LOCATION), AdminTokenConstant.KEY_PASSWORD.toCharArray());
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        // 密钥别名
        converter.setKeyPair(keyStoreKeyFactory.getKeyPair(AdminTokenConstant.KEY_PASSWORD));
        return converter;
    }

    /**
     * 声明 ClientDetails实现
     *
     * @return
     */
    @Bean
    public ClientDetailsService clientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }

    /**
     * 配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,
     * 你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 直接读取数据库,需要保证数据库配置有客户端信息(oauth_client_details),否则资源服务器无法获取认证数据
        clients.withClientDetails(clientDetailsService());
    }

    /**
     * 配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services),还有token的存储方式(tokenStore)
     *
     * @param endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.tokenStore(tokenStore()).tokenEnhancer(jwtAccessTokenConverter()).authenticationManager(authenticationManager);
        // 配置tokenServices参数
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(endpoints.getTokenStore());
        tokenServices.setSupportRefreshToken(false);
        tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
        tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
        tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30));
        endpoints.tokenServices(tokenServices);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        // 允许表单认证
        security.allowFormAuthenticationForClients()
                // 开启/oauth/token_key验证端口无权限访问
                .tokenKeyAccess("permitAll()")
                // 开启/oauth/check_token验证端口认证权限访问
                .checkTokenAccess("permitAll()");
    }
}

SecurityConfiguration文件

package com.weige.javaskillpoint.config;

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.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.annotation.Resource;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Resource
    private UserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

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

    /**
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 所有请求都加入HttpSecurity(多个HttpSecurity过滤)
        http.requestMatchers().anyRequest()
                // 开放/oauth/开头的所有请求
                .and().authorizeRequests().antMatchers("/oauth/**").permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 注入自定义的UserDetailsService,采用BCrypt加密
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
}

UserDetailServiceImpl文件

package com.weige.javaskillpoint.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.stereotype.Service;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class UserDetailServiceImpl implements UserDetailsService {

    /**
     * 根据用户名加载用户信息
     *
     * @param username 用户名
     * @return 用户详情
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 这里可以连接数据库 我这里写假数据
        // 有两个用户(username:user,password:user;username:admin,password:admin)
        Map<String, String> map = new HashMap<>();
        map.put("user", "USER");
        map.put("admin", "ADMIN");
        String passWord = "$2a$10$b7cqpfkMLYE2H5wxMluGQOrXo76ZfaAALEaemrfFLwDvvHuJuvX/2";
        if (username.equals("admin")) {
            passWord = "$2a$10$b7cqpfkMLYE2H5wxMluGQOrXo76ZfaAALEaemrfFLwDvvHuJuvX/2";
        }

        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        // 获取用户的授权
        String s = map.get(username);
        // Spring Security 中权限名称必须满足ROLE_XXX
        GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(StringUtils.join("ROLE_", s));
        grantedAuthorities.add(grantedAuthority);
        log.info("granted authorities :{} ", grantedAuthorities);
        return new User(username, passWord, grantedAuthorities);
    }
}

JavaSkillPointApplication文件

package com.weige.javaskillpoint;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.weige.javaskillpoint.dao")
public class JavaSkillPointApplication {

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

JavaSkillPointApplicationTests测试文件

package com.weige.javaskillpoint;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.util.HashMap;
import java.util.Map;

@SpringBootTest
class JavaSkillPointApplicationTests {

    @Test
    public void testCreateJwt() throws Exception {
        //证书⽂件
        String key_location = "kaikeba.jks";
        // 密钥库密码
        String keystore_password = "kaikeba";
        // 访问证书路径
        ClassPathResource resource = new ClassPathResource(key_location);
        // 密钥⼯⼚
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource, keystore_password.toCharArray());
        // 密钥的密码,此密码和别名要匹配
        String keypassword = "kaikeba";
        //密钥别名
        String alias = "kaikeba";
        // 密钥对(密钥和公钥)
        KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias, keypassword.toCharArray());
        // 私钥
        RSAPrivateKey aPrivate = (RSAPrivateKey) keyPair.getPrivate();
        // 定义payload信息
        Map<String, Object> tokenMap = new HashMap<String, Object>();
        tokenMap.put("id", "123");
        tokenMap.put("name", "malong");
        tokenMap.put("roles", "r01,r02");
        tokenMap.put("ext", "1");
        // ⽣成jwt令牌
        Jwt jwt = JwtHelper.encode(new ObjectMapper().writeValueAsString(tokenMap), new RsaSigner(aPrivate));
        // 取出jwt令牌
        String token = jwt.getEncoded();
        System.out.println(token);
    }

    @Test
    public void testVerify() {
        //jwt令牌
        String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHQiOiIxIiwicm9sZXMiOiJyMDEscjAyIiwibmFtZSI6Im1hbG9uZyIsImlkIjoiMTIzIn0.X68eLdVW69xJQGcICVZapWUay-vs5Fkv0cYRQoymuagR95G4bOkJ4d3rer6ko9gMS55htLmAmv3HkzeCOOE4R6fXJcGzyuDWY9D9lC7ca0-AX4okS4iLeTQAf53AIDLM3d1DQbRdJYrdDbSwhXZXIaaaaQNiVpnN3kGXK6YB7f1ohlEURFT50bf7lKVyc8xoJX4-ojLfZiWP2C8Ov84yEOc-Q2t9kRwPtPqQjial69b0FmPqtdfPbhJG66NAQdikRCxqHKSgb6QSMc9AF9AV4RluaPixoGIpufouWpWXyk9PvK-QJwwUJgM11emDTX2wv8lf5VXH-Wdg_1Jc_uwjLA";
        String publickey = "-----BEGIN PUBLIC KEY-----\n" +
                "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlym6EoAWxPYpTQHuWfqn\n" +
                "WqRkiUDrD+CyXM3Rzgtfi6ofudPDyEY5JbFcKIcfwjdQo+VLF0dIvyJA2csHhQIS\n" +
                "vZBspsE9cjz24GOuYSYufYtwOVcbjsvZ3JQaJahR6rBXzPTjvaiu1KtFtEJavWQ8\n" +
                "S9nhZ4MEMImW8r+Qphd0R4OB8KHrWoztYNznWpSaaH3QB446tQyyBre7tEPp3E6J\n" +
                "Xq+ApI5UKBpTdHW7b7MSJW836sYm8g5XfaXF667rlkdS03q3B182hUcghRY+fAyS\n" +
                "OtD2A0ib7XlrNADPL9X3dazWCYTnbNDswiqfpoDrbLy0PWFeIq09amL+E1ivKJZS\n" +
                "9wIDAQAB\n" +
                "-----END PUBLIC KEY-----";
        //校验jwt
        Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(publickey));
        //获取jwt原始内容
        String claims = jwt.getClaims();
        System.out.println(claims);
    }

    @Test
    public void getEnPassword(){
        String encode = new BCryptPasswordEncoder().encode("123456");
        System.out.println(encode);
    }
}

这里别忘记了生成的JWT公钥私钥文件 放到reources目录下即可

搭建资源项目 项目目录 Spring Security实现OAuth2协议及实战

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 https://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.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.weige</groupId>
    <artifactId>java-skill-point</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>java-skill-point</name>
    <description>java-skill-point</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR5</spring-cloud.version>
    </properties>
    <dependencies>
        <!--Springboot项目自带 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--Springboot Web项目 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>

        <!--Mysql依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- mybatis 用于接口注解快速开发(@Insert @Select @Update) -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <!--用于扫描mybatis注解 @Mapper @MapperScan-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>o
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <!-- hutool  -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.20</version>
        </dependency>

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

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

    </dependencies>

    <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>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

application.yml

server:
  port: 8084

spring:
  # mysql连接信息
  datasource:
    # mysql8之后
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://43.143.132.109:3306/index?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
    username: root
    password: Weikai19991015.

security:
  oauth2:
    resource:
      jwt:
        key-uri: http://localhost:8888/oauth/token_key

Config文件

package com.weige.javaskillpoint.config;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class Config {
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }
}

JwtConfig文件

package com.weige.javaskillpoint.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.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.util.FileCopyUtils;

import java.io.IOException;

@Configuration
public class JwtConfig {
    public static final String public_cert = "public.key";
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Bean
    @Qualifier("tokenStore")
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter);
    }

    @Bean
    protected JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        Resource resource = new ClassPathResource(public_cert);
        String publicKey;
        try {
            publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 设置校验公钥
        converter.setVerifierKey(publicKey);
        // 设置证书签名密码,否则报错
        converter.setSigningKey("kaikeba");
        return converter;
    }
}

ResourceServerConfiguration文件

package com.weige.javaskillpoint.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    @Autowired
    private TokenStore tokenStore;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests()
                .antMatchers("/user/**")
                .permitAll()
                .antMatchers("/book/**").hasRole("admin")
                .antMatchers("/**")
                .authenticated();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenStore(tokenStore);
    }
}

HelloController文件

package com.weige.javaskillpoint.controller;

import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;


@RestController
@RequestMapping("/cms")
public class HelloController {

    @GetMapping("/getCurrentUser")
    public Object getCurrentUser(Authentication authentication) {
        return authentication;
    }

    @GetMapping("/index/user")
    @PreAuthorize("hasRole('ROLE_USER')")
    public String index() {
        return "index/user";
    }


    @GetMapping("/index/admin")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public String index2() {
        return "index/admin";
    }
}

JavaSkillPointApplication文件

package com.weige.javaskillpoint;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.weige.javaskillpoint.dao")
public class JavaSkillPointApplication {

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

}

这里别忘记了生成的JWT公钥文件 放到reources目录下即可

两个项目都创建完成,在启动项目时。要注意先后顺序,先启动授权项目,在启动资源项目。因为资源项目会请求授权项目中的token中JTW对应的公钥解析是否对应私钥。 Spring Security实现OAuth2协议及实战

3.3 授权接口调用并测试资源接口

密码模式: 申请令牌 PostMan请求 localhost:8888/oauth/token?grant_type=password&username=user&password=123456&scope=read Spring Security实现OAuth2协议及实战 Spring Security实现OAuth2协议及实战 Spring Security实现OAuth2协议及实战 Spring Security实现OAuth2协议及实战 资源项目接口测试:

Spring Security实现OAuth2协议及实战 资源服务器接口调用 USER http://localhost:8084/cms/index/user Spring Security实现OAuth2协议及实战 资源服务器接口调用 ADMIN http://localhost:8084/cms/index/admin Spring Security实现OAuth2协议及实战 Spring Security实现OAuth2协议及实战 资源服务器接口调用 ADMIN http://localhost:8084/cms/index/admin Spring Security实现OAuth2协议及实战

3.4 资源项目中添加用户登录接口 获取令牌(token)返回给前端

资源项目添加几个类: Spring Security实现OAuth2协议及实战 AdminConstants文件

package com.weige.javaskillpoint.constant;

public interface AdminConstants {

    /**
     * 请求头key
     */
    String AUTHORIZATION_KEY = "Authorization";

    /**
     * Basic
     */
    String BASIC_KEY = "Basic ";

    /**
     * 用户名key
     */
    String USERNAME_KEY = "username";

    /**
     * 密码key
     */
    String PASSWORD_KEY = "password";

    /**
     * 认证类型key
     */
    String GRANT_TYPE_KEY = "grant_type";

    /**
     * 授权范围key
     */
    String SCOPE_KEY = "scope";

    /**
     * 分隔符
     */
    String SPLIT = ":";
}

LoginController文件

package com.weige.javaskillpoint.controller;

import com.weige.javaskillpoint.entity.LoginRequestDTO;
import com.weige.javaskillpoint.service.LoginService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("/user")
public class LoginController {

    @Resource
    private LoginService loginService;

    /**
     * 用户登录
     *
     * @param loginRequestDTO 登录请求DTO
     * @return org.springframework.http.ResponseEntity
     */
    @PostMapping("/login")
    public ResponseEntity<OAuth2AccessToken> login(@RequestBody LoginRequestDTO loginRequestDTO) {
        return loginService.login(loginRequestDTO);
    }

}

LoginRequestDTO文件

package com.weige.javaskillpoint.entity;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;


@Setter
@Getter
@ToString
public class LoginRequestDTO {

    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;
}

LoginService文件

package com.weige.javaskillpoint.service;

import com.weige.javaskillpoint.entity.LoginRequestDTO;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.OAuth2AccessToken;

public interface LoginService {


    /**
     * 用户登录
     *
     * @param loginRequestDTO 登录请求DTO
     * @return org.springframework.http.ResponseEntity
     */
    ResponseEntity<OAuth2AccessToken> login(LoginRequestDTO loginRequestDTO);
}

LoginServiceImpl文件

package com.weige.javaskillpoint.service.impl;

import com.weige.javaskillpoint.constant.AdminConstants;
import com.weige.javaskillpoint.entity.LoginRequestDTO;
import com.weige.javaskillpoint.service.LoginService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.Base64;
import java.util.Collections;

@Service
public class LoginServiceImpl implements LoginService {

    @Resource
    private RestTemplate restTemplate;


    @Resource
    private OAuth2ClientProperties oAuth2ClientProperties;

    @Resource
    private OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails;

    /**
     * 用户登录
     *
     * @param loginRequestDTO 登录请求DTO
     * @return org.springframework.http.ResponseEntity
     */
    @Override
    public ResponseEntity<OAuth2AccessToken> login(LoginRequestDTO loginRequestDTO) {

        HttpHeaders headers = new HttpHeaders();
        headers.set(AdminConstants.AUTHORIZATION_KEY, StringUtils.join(AdminConstants.BASIC_KEY, Base64.getEncoder().encodeToString(StringUtils.join(oAuth2ClientProperties.getClientId(), AdminConstants.SPLIT, oAuth2ClientProperties.getClientSecret()).getBytes())));
        // 组装请求参数
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>(4);
        map.put(AdminConstants.USERNAME_KEY, Collections.singletonList(loginRequestDTO.getUsername()));
        map.put(AdminConstants.PASSWORD_KEY, Collections.singletonList(loginRequestDTO.getPassword()));
        map.put(AdminConstants.GRANT_TYPE_KEY, Collections.singletonList(oAuth2ProtectedResourceDetails.getGrantType()));
        map.put(AdminConstants.SCOPE_KEY, oAuth2ProtectedResourceDetails.getScope());
        // 请求到授权服务器,将授权完的用户信息存到授权服务器,并申请令牌
        return restTemplate.exchange(oAuth2ProtectedResourceDetails.getAccessTokenUri(), HttpMethod.POST, new HttpEntity(map, headers), OAuth2AccessToken.class);
    }

}

application.yml文件

server:
  port: 8084

spring:
  # mysql连接信息
  datasource:
    # mysql8之后
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://43.143.132.109:3306/index?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
    username: root
    password: Weikai19991015.

security:
  oauth2:
    resource:
      jwt:
        key-uri: http://localhost:8888/oauth/token_key
    client:
      access-token-uri: http://localhost:8888/oauth/token #令牌端点
      user-authorization-uri: http://localhost:8888/oauth/authorize #授权端点
      client-id: cms
      client-secret: 123456
      grant-type: password
      scope: read,write

调用资源服务登录接口:PostMan调用 http://localhost:8084/user/login Spring Security实现OAuth2协议及实战 拿到获取的token访问资源服务的接口 http://localhost:8084/cms/index/user Spring Security实现OAuth2协议及实战

用admin登录 Spring Security实现OAuth2协议及实战 拿到获取的token访问资源服务的接口 http://localhost:8084/cms/index/admin Spring Security实现OAuth2协议及实战

完结撒花 !