oauth2.0系统学习3-简单搭建spring sercurity+oauth2.0的框架

oauth2.0系统学习3-简单搭建spring sercurity+oauth2.0的框架


一、简单搭建spring sercurity+oauth2.0 的框架

  1. oauth_parent:父工程
  2. oauth-authorizationServer:认证服务器
  3. oauth-ResourceServer:资源服务器

在这里插入图片描述

oauth_parent:父工程代码(就是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.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>oauth_parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>oauth_parent</name>

    <packaging>pom</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

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

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>


            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>

            <dependency>
                <groupId>javax.interceptor</groupId>
                <artifactId>javax.interceptor-api</artifactId>
                <version>1.2</version>
            </dependency>

            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.47</version>
            </dependency>

            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.0</version>
            </dependency>



            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-jwt</artifactId>
                <version>1.0.10.RELEASE</version>
            </dependency>


            <dependency>
                <groupId>org.springframework.security.oauth.boot</groupId>
                <artifactId>spring-security-oauth2-autoconfigure</artifactId>
                <version>2.1.3.RELEASE</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

二、搭建一个认证服务器 oauth-authorizationServer

在这里插入图片描述

2.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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>oauth_parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>oauth-authorizationserver</artifactId>
    <name>oauth-authorizationserver</name>

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


        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-commons</artifactId>
        </dependency>

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

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

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


        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>


        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

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

</project>

2.2、application.properties

spring.application.name=oauth-authorizationServer
server.port=8081

spring.datasource.url = jdbc:mysql://localhost:3306/testoauth?useUnicode=true
spring.datasource.username=root
spring.datasource.password=ylx123
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

2.3、UserAuthorizeBean

package com.example.oauthauthorizationserver.business.bean;

import lombok.Data;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/13  22:23
 * @Verson 1.0
 **/

@Data
public class UserBean {
    private Long id;
    private String userName;
    private String password;

}

2.4、UserAuthorizeBean

package com.example.oauthauthorizationserver.business.bean;

import lombok.Data;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/13  22:18
 * @Verson 1.0
 **/

@Data
public class UserAuthorizeBean {
    private Long id;
    private Long userId;
    private String authorizeCode;

}

2.5、AuthorizationServer

package com.example.oauthauthorizationserver.business.config;

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.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.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
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;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/13  22:23
 * @Verson 1.0
 **/

@Configuration
@EnableAuthorizationServer // 配置授权服务。
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

    // 用来配置客户端详情服务
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 这里是第三方合作用户的客户id,秘钥的配置
        // 使用in-memory存储
        clients.inMemory()
                // client_id,用户账号
                .withClient("c1")
                // 客户端密钥
                .secret(new BCryptPasswordEncoder().encode("secret"))
                // 资源列表,资源标识
                .resourceIds("res1")
                // 授权类型(4种)
                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit",
                        "refresh_token")
                // 客戶端允许的授权范围
                .scopes("all")
                // false跳转到授权页面,让用户点击授权,如果是true,相当于自动点击授权,就不跳转授权页面
                .autoApprove(false)//
                // 加上验证回调地址,返回授权码信息
                .redirectUris("http://www.baidu.com");

        // 如果有多个用户,配置多个客户详情
        // .and().withClient()

    }

    @Autowired
    // 令牌存储策略
    private TokenStore tokenStore;
    @Autowired
    // 客户端详情服务,也就是configure(ClientDetailsServiceConfigurer clients)方法
    private ClientDetailsService clientDetailsService;

    // 令牌管理服务
    @Bean
    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices service = new DefaultTokenServices();
        service.setClientDetailsService(clientDetailsService);
        service.setSupportRefreshToken(true);// 支持刷新
        service.setTokenStore(tokenStore);// 令牌存储
        service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
        service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
        return service;
    }

    @Autowired
    // 授权码服务
    private AuthorizationCodeServices authorizationCodeServices;
    @Autowired
    // 认证管理
    private AuthenticationManager authenticationManager;
    @Autowired
    // 令牌管理服务
    private AuthorizationServerTokenServices authorizationServerTokenServices;

    @Override
    // 用来配置令牌(token)的访问端点
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                // 密码模式需要
                .authenticationManager(authenticationManager)
                // 授权码模式需要
                .authorizationCodeServices(authorizationCodeServices)
                // 令牌管理服务
                .tokenServices(authorizationServerTokenServices).allowedTokenEndpointRequestMethods(HttpMethod.POST);// 允许post提交
    }

    @Bean
    // 授权码服务器
    public AuthorizationCodeServices authorizationCodeServices() {
        // 授权码模式的授权码采用内存方式存储
        return new InMemoryAuthorizationCodeServices();
    }

    @Override
    // 用来配置令牌端点的安全约束,拦截规则
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {

        security
                // 提供公有密匙的端点,如果你使用JWT令牌的话, 允许
                .tokenKeyAccess("permitAll()")
                // oauth/check_token:用于资源服务访问的令牌解析端点,允许
                .checkTokenAccess("permitAll()")
                // 表单认证,申请令牌
                .allowFormAuthenticationForClients();
    }

    @Bean
    // 令牌存储策略
    public TokenStore tokenStore() {
        // 内存存储,普通令牌
        return new InMemoryTokenStore();
    }
}

2.6、WebSecurityConfig

package com.example.oauthauthorizationserver.business.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.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/13  22:24
 * @Verson 1.0
 **/

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    // 认证管理器
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    // 密码编码器
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 安全拦截机制
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.
                csrf().disable().

                authorizeRequests().
                antMatchers("/admin/p1").
                hasAnyAuthority("p1").

                antMatchers("/user/p2").
                hasAnyAuthority("p2").
                antMatchers("/login*")
                .permitAll().
                anyRequest().
                authenticated().
                and().
                formLogin();

    }
}

2.7、UserDao

package com.example.oauthauthorizationserver.business.dao;

import com.example.oauthauthorizationserver.business.bean.UserAuthorizeBean;
import com.example.oauthauthorizationserver.business.bean.UserBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/13  22:26
 * @Verson 1.0
 **/

@Repository
public class UserDao {
    @Autowired
    JdbcTemplate jdbcTemplate;

    // 根据账号查询用户信息
    public UserBean getUserByUsername(String username) {
        String sql = "select * from user where user_name = ?";
        // 连接数据库查询用户
        List<UserBean> list = jdbcTemplate.query(sql, new Object[] { username },
                new BeanPropertyRowMapper<>(UserBean.class));
        if (list != null && list.size() == 1) {
            return list.get(0);
        }
        return null;
    }

    // 根据用户id查询用户权限
    public List<String> getAuthorize(Long userId) {
        String sql = "SELECT * FROM user_authorize WHERE user_id =?";
        List<UserAuthorizeBean> list = jdbcTemplate.query(sql, new Object[] { userId },
                new BeanPropertyRowMapper<>(UserAuthorizeBean.class));
        List<String> authorizes = new ArrayList<>();
        list.forEach(c -> authorizes.add(c.getAuthorizeCode()));
        return authorizes;
    }

}

2.8、SpringDataUserDetailsService

package com.example.oauthauthorizationserver.business.service;

import com.example.oauthauthorizationserver.business.bean.UserBean;
import com.example.oauthauthorizationserver.business.dao.UserDao;
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.List;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/13  22:39
 * @Verson 1.0
 **/

@Service
public class SpringDataUserDetailsService implements UserDetailsService {

    @Autowired
    UserDao userDao;

    // 根据账号查询用户信息,
    // 通过@Service将SpringDataUserDetailsService注入容器,通过UserDetailsService接口表明该类的类型是UserDetailsService
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 将来连接数据库根据账号查询用户信息
        UserBean bean = userDao.getUserByUsername(username);
        if (bean == null) {
            // 如果用户查不到,返回null,由provider来抛出异常
            return null;
        }

        // 查询当前数据库的用户资源权限
        List<String> authorize = userDao.getAuthorize(bean.getId());
        String[] authorizeArr = new String[authorize.size()];
        authorize.toArray(authorizeArr);

        // 添加权限
        UserDetails userDetails = User.withUsername(bean.getUserName()).password(bean.getPassword())
                .authorities(authorizeArr).build();
        return userDetails;
    }
}

2.9、mysql数据库中创建两张表

user:用户账号密码表

在这里插入图片描述

user_authorize:用户资源授权表

在这里插入图片描述

2.10、理论,解读代码

L1. OAuth2.0授权服务器的开启

  1. @EnableAuthorizationServer注解

  2. 继承AuthorizationServerConfigurerAdapter

在这里插入图片描述

L2. AuthorizationServerConfigurerAdapter:

  1. 要配置以下几个类,通过重写以下方法来实现

在这里插入图片描述

L3. ClientDetailsServiceConfigurer

  1. 用来配置客户端详情服务(ClientDetailsService )
    1. ClientDetailsService负责查找ClientDetails
  2. 可通过内存或者数据库来配置
  3. 客户端详情服务
    1. 第三方服务器来授权服务器申请访问资源。通过授权后,颁发clientId,secret给第三方服务器,表示第三方服务器是信任的服务器。

ClientDetails重要属性列表:

  1. clientId:标识客户的Id

  2. secret:客户端秘钥

  3. scope:限制客户端的访问范围

    1. 默认空:拥有全部的访问范围。
  4. authorizedGrantTypes:此客户端可以使用的授权类型

    1. 默认为空
  5. authorities:客户端可以使用的权限

    1. 基于SpringSecurityauthorities

ClientDetails介绍

  1. 能够在应用程序运行的时候进行更新
  2. 可以通过访问底层的存储服务
    1. 客户端详情存储在一个关系数据库的表中
    2. 实现方式
      1. 使用JdbcClientDetailsService
      2. 自己实现ClientRegistrationService接口
      3. 自己实现ClientDetailsService接口

具体代码

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 这里是第三方合作用户的客户id,秘钥的配置
        // 使用in-memory存储
        clients.inMemory()
                // client_id,用户账号
                .withClient("c1")
                // 客户端密钥
                .secret(new BCryptPasswordEncoder().encode("secret"))
                // 资源列表,资源标识
                .resourceIds("res1")
                // 授权类型(4种)
                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit",
                        "refresh_token")
                // 客戶端允许的授权范围
                .scopes("all")
                // false跳转到授权页面,让用户点击授权,如果是true,相当于自动点击授权,就不跳转授权页面
                .autoApprove(false)//
                // 加上验证回调地址,返回授权码信息
                .redirectUris("http://www.baidu.com");

        // 如果有多个用户,配置多个客户详情
        // .and().withClient()

    }

L4. AuthorizationServerTokenServices

  1. 令牌管理服务
  2. 定义管理令牌的操作
  3. 令牌
    1. 用来加载身份信息
    2. 包含了这个令牌的相关权限。
  4. 这个接口的实现,则需要继承DefaultTokenServices

在这里插入图片描述

DefaultTokenServices

  1. 可以修改令牌的格式
  2. 可以修改令牌的存储。
  3. 默认的,当它尝试创建一个令牌使用随机值来进行填充的,除了持久化令牌是委托一个TokenStore接口来实现以外,这个类几乎帮你做了所有的事情。

TokenStore

  1. 默认实现:InMemoryTokenStore

    1. 所有的令牌是被保存在了内存中
    2. 它可以完美的工作在单服务器上(即访问并发量压力不大的情况下,并且它在失败的时候不会进行备份)
  2. JdbcTokenStore

    1. 基于JDBC的实现版本,令牌会被保存进关系型数据库
    2. 注意点:需要把"spring-jdbc"这个依赖加入到你的classpath当中。
  3. JwtTokenStore

    1. 它可以把令牌相关的数据进行编码(因此对于后端服务来说,它不需要进行存储,这将是一个重大优势)
    2. 缺点:
      1. 撤销一个已经授权令牌非常困难,所以它通常用来处理一个生命周期较短的令牌以及撤销刷新令牌(refresh_token)
      2. 这个令牌占用的空间会比较大,如果你加入了比较多用户凭证信息。JwtTokenStore不会保存任何数据,但是它在转换令牌值以及授权信息方面与DefaultTokenServices所扮演的角色是一样的。

在这里插入图片描述

L5. AuthorizationServerEndpointsConfigurer

  1. 可以完成令牌服务
  2. 可以完成令牌endpoint配置

配置授权类型(GrantTypes)

  1. authenticationManager:认证管理器
    1. 选择密码(password)授权类型的时候,请设置这个属性注入一个AuthenticationManager对象。
  2. userDetailsService:
    1. 设置UserDetailsService接口的实现类(用户登录验证)
    2. 或者你可以把这个东西设置到全局域上面去(例如GlobalAuthenticationManagerConfigurer这个配置对象),当你设置了这个之后,那么"refresh_token"即刷新令牌授权类型模式的流程中就会包含一个检查,用来确保这个账号是否仍然有效。
  3. authorizationCodeServices:设置授权码服务
    1. 用于"authorization_code"授权码类型模式
  4. implicitGrantService:设置隐式授权模式,用来管理隐式授权模式的状态。
  5. tokenGranter:
    1. 设置TokenGranter接口实现,授权将会交由你来完全掌控,并且会忽略掉上面的这几个属性
    2. 这个属性一般是用作拓展用途的,即标准的四种授权模式已经满足不了你的需求的时候,才会考虑使用这个。

具体代码

@Override
// 用来配置令牌(token)的访问端点
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints
            // 密码模式需要
            .authenticationManager(authenticationManager)
            // 授权码模式需要
            .authorizationCodeServices(authorizationCodeServices)
            // 令牌管理服务
            .tokenServices(authorizationServerTokenServices).allowedTokenEndpointRequestMethods(HttpMethod.POST);// 允许post提交
}

配置授权端点的URL(EndpointURLs)

  1. AuthorizationServerEndpointsConfigurer有一个pathMapping()的方法用来配置端点URL链接
  2. 它有两个参数
    1. 第一个参数:String类型的,这个端点URL的默认链接。
    2. 第二个参数:String类型的,你要进行替代的URL链接。
    3. 以上的参数都将以"/"字符为开始的字符串,
    4. 框架的默认URL链接如下列表,可以作为这个pathMapping()方法的第一个参数
      1. /oauth/authorize:授权端点。
      2. /oauth/token:令牌端点。
      3. /oauth/confirm_access:用户确认授权提交端点。
      4. /oauth/error:授权服务错误信息端点。
      5. /oauth/check_token:用于资源服务访问的令牌解析端点。
      6. /oauth/token_key:提供公有密匙的端点,如果你使用JWT令牌的话。需要注意的是授权端点这个URL应该被SpringSecurity保护起来只供授权用户访问。

L7. authorizationCodeServices

  1. 授权码服务的存储方式

具体代码

@Bean
// 授权码服务器
public AuthorizationCodeServices authorizationCodeServices() {
    // 授权码模式的授权码采用内存方式存储
    return new InMemoryAuthorizationCodeServices();
}

L8. AuthorizationServerSecurityConfigurer

  1. 令牌端点的安全约束

  2. tokenkey:/oauth/token_key端点完全公开。

  3. checkToken:/oauth/check_token端点完全公开。

  4. allowFormAuthenticationForClients:允许表单认证

具体代码

@Override
// 用来配置令牌端点的安全约束,拦截规则
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {

    security
            // 提供公有密匙的端点,如果你使用JWT令牌的话, 允许
            .tokenKeyAccess("permitAll()")
            // oauth/check_token:用于资源服务访问的令牌解析端点,允许
            .checkTokenAccess("permitAll()")
            // 表单认证,申请令牌
            .allowFormAuthenticationForClients();
}

2.11、验证认证服务器

001)、授权码模式

http://localhost:8081/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com

需要先登录(Security的原因)

在这里插入图片描述

用户授权

在这里插入图片描述

返回授权码

在这里插入图片描述

请求token

http://localhost:8081/oauth/token?client_id=c1&client_secret=secret&grant_type=authorization_code&code=Nsitob&redirect_uri=http://www.baidu.com

在这里插入图片描述

002)、简化模式

http://localhost:8081/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com

在这里插入图片描述

在这里插入图片描述

003) 、密码模式

http://localhost:8081/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=admin&password=admin

在这里插入图片描述

004) 、客户端模式

http://localhost:8081/oauth/token?client_id=c1&client_secret=secret&grant_type=client_credentials

在这里插入图片描述

三、搭建一个资源服务器 oauth-ResourceServer

在这里插入图片描述

3.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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>oauth_parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>oauth-resourceserver</artifactId>
    <name>oauth-resourceserver</name>


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

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

    </dependencies>

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

</project>

3.2、application.properties

spring.application.name=oauth-ResourceServer
server.port=8082

3.3、ResouceServerConfig

package com.example.oauthresourceserver.business.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
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.ResourceServerTokenServices;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/13  23:08
 * @Verson 1.0
 **/

@Configuration
@EnableResourceServer
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {

    // 授权服务的资源列表
    public static final String RESOURCE_ID = "res1";

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources
                // 资源 id
                .resourceId(RESOURCE_ID)
                // 令牌服务
                .tokenServices(tokenService())
                .stateless(true);
    }

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

        http

                .authorizeRequests().antMatchers("/**")
                // 所有的访问,授权访问都要是all,和认证服务器的授权范围一一对应
                .access("#oauth2.hasScope('all')")
                //去掉防跨域攻击
                .and().csrf().disable()
                //session管理
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Bean
    // 资源服务令牌解析服务
    public ResourceServerTokenServices tokenService() {
        // 使用远程服务请求授权服务器校验token,必须指定校验token 的url、client_id,client_secret
        RemoteTokenServices service = new RemoteTokenServices();
        service.setCheckTokenEndpointUrl("http://localhost:8081/oauth/check_token");
        service.setClientId("c1");
        service.setClientSecret("secret");
        return service;
    }

}

3.4、WebSecurityConfig

package com.example.oauthresourceserver.business.config;

import org.springframework.context.annotation.Bean;
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/13  23:09
 * @Verson 1.0
 **/

//由于Spring boot starter自动装配机制,这里无需使用@EnableWebSecurity
//@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // 方法授权
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    // 密码编码器,不加密
    @Bean
    public PasswordEncoder passwordEncoder() {
        // return NoOpPasswordEncoder.getInstance(); // 不加密
        return new BCryptPasswordEncoder();// BCryptPasswordEncoder加密
    }

    // web url 拦截规则
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()

                .anyRequest().authenticated()// 所有其他请求必须认证通过
                .and().formLogin().loginPage("/login").successForwardUrl("/login-success")// 自定义登录成功的页面地址
                .permitAll().and().logout().permitAll().and()// 会话管理
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);// 会话管理
    }
}

3.5、LoginController

package com.example.oauthresourceserver.business.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/13  23:10
 * @Verson 1.0
 **/

@RestController
public class LoginController {

    @RequestMapping(value = "/login-success")
    public String loginSuccess() {
        return getUsername() + " login-success 登录成功";
    }

    /**
     * 测试资源1
     *
     * @return
     */
    @GetMapping(value = "/admin/p1")
    @PreAuthorize("hasAuthority('p1')")//拥有p1权限才可以访问
    public String r1() {
        return " /admin/p1 " + getUsername() + "访问资源1";
    }

    /**
     * 测试资源2
     *
     * @return
     */
    @GetMapping(value = "/user/p2")
    @PreAuthorize("hasAuthority('p2')")//拥有p2权限才可以访问
    public String r2() {
        return "/user/p2 " + getUsername() + "访问资源2";
    }

    // 获取当前用户信息
    private String getUsername() {
        String username = null;
        // 当前认证通过的用户身份
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        // 用户身份
        Object principal = authentication.getPrincipal();
        if (principal == null) {
            username = "匿名";
        }
        if (principal instanceof UserDetails) {
            UserDetails userDetails = (UserDetails) principal;
            username = userDetails.getUsername();
        } else {
            username = principal.toString();
        }
        return username;
    }
}

002)、代码解读和理论

L1. OAuth2.0资源服务器的开启

  1. @EnableResourceServer注解
    1. 会自动增加了一个类型为OAuth2AuthenticationProcessingFilter的过滤器链
  2. 继承ResourceServerConfigurerAdapter

在这里插入图片描述

L2. ResourceServerSecurityConfigurer

  1. tokenServices:

    1. ResourceServerTokenServices类的实例
    2. 用来实现令牌服务
  2. tokenStore:

    1. TokenStore类的实例,指定令牌如何访问,与tokenServices配置可选
  3. resourceId

    1. 这个资源服务的ID,这个属性是可选的,推荐设置并在授权服务中进行验证
  4. tokenExtractor

    1. 令牌提取器用来提取请求中的令牌

具体代码:

@Override
public void configure(ResourceServerSecurityConfigurer resources) {
    resources
            // 资源 id
            .resourceId(RESOURCE_ID)
            // 令牌服务
            .tokenServices(tokenService())
            .stateless(true);
}

L3. HttpSecurity配置这个与Spring Security类似

具体代码:

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

    http

            .authorizeRequests().antMatchers("/**")
            // 所有的访问,授权访问都要是all,和认证服务器的授权范围一一对应
            .access("#oauth2.hasScope('all')")
            //去掉防跨域攻击
            .and().csrf().disable()
            //session管理
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

L4. ResourceServerTokenServices

  1. DefaultTokenServices

    1. 授权服务和资源服务在同一个应用程序上可以使用
    2. 在资源服务器本地配置令牌存储、解码、解析方式使用
  2. RemoteTokenServices

    1. 资源服务器通过HTTP请求来解码令牌
    2. 请求端点:/oauth/check_token

具体代码:

@Bean
// 资源服务令牌解析服务
public ResourceServerTokenServices tokenService() {
    // 使用远程服务请求授权服务器校验token,必须指定校验token 的url、client_id,client_secret
    RemoteTokenServices service = new RemoteTokenServices();
    service.setCheckTokenEndpointUrl("http://localhost:8081/oauth/check_token");
    service.setClientId("c1");
    service.setClientSecret("secret");
    return service;
}

003)、测试

获取admin账号token

http://localhost:8081/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=admin&password=admin

在这里插入图片描述

获取user账号token

http://localhost:8081/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=user&password=user

在这里插入图片描述

检查token的信息:token参数为第一步获取时候的值

admin:

http://localhost:8081/oauth/check_token?token=474beb14-45b2-46f6-a4c7-361611730f76

在这里插入图片描述

user:

http://localhost:8081/oauth/check_token?token=10d95d5d-1e45-4c6c-a1aa-7031970b7dcc

在这里插入图片描述

admin请求可以访问的资源

头部信息中添加 Authorization,值为:“Bearer ” + token

在这里插入图片描述

admin请求不可以访问的资源

头部信息中添加 Authorization,值为:“Bearer ” + token

在这里插入图片描述

user请求可以访问的资源

头部信息中添加 Authorization,值为:“Bearer ” + token

在这里插入图片描述

user请求不可以访问的资源

头部信息中添加 Authorization,值为:“Bearer ” + token

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值