Spring Security oauth2(一)快速入门,搭建授权服务器

在进行Spring Security oauth2快速入门之前,我们先来了解一些基本的概念

1.基本概念

1.1.OAuth2.0是什么?

1.1.1.什么是OAuth

OAuth是一种开放协议, 允许用户让第三方应用以安全且标准的方式获取该用户在某一网站,移动或者桌面应用上存储的秘密的资源(如用户个人信息,照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

1.1.2.OAuth2.0和OAuth的关系

OAuth协议(RFC5849)作为一个指导性文档发布,是一个小社区的工作成果。
OAuth2.0在OAuth的部署经验之上构建,也包括其他使用案例以及从更广泛的IETF社区收集到的可扩展性需求。
OAuth 2.0协议不向后兼容OAuth 1.0。这两个版本可以在网络上共存,实现者可以选择同时支持他们。

1.1.3.OAuth2.0应用

OAuth2.0在安全性方面做了比较大的提高,简单来说OAuth2.0就是一种授权协议,可以用来授权,随意点个网站,如下图这些网站,用户不想注册,就可以用微信、支付宝登录,就是场景是很常见的,也是OAuth2.0的应用。
在这里插入图片描述在这里插入图片描述

1.2.OAuth2.0原理

OAuth2.0是一种授权机制,正常情况,不使用OAuth2.0等授权机制的系统,客户端是可以直接访问资源服务器的资源的,为了用户安全访问数据,在访问中间添加了Access Token机制。客户端需要携带Access Token去访问受到保护的资源。所以OAuth2.0确保了资源不被恶意客户端访问,从而提高了系统的安全性。

1.3.OAuth2.0的角色

a.授权服务器(AuthorizationServer): 在成功验证资源所有者且获得授权后颁发访问令牌给客户端的服务器,使得授权客户端应用能够访问资源拥有者所拥有的资源。

b.资源服务器(ResourceServer):托管受保护资源的服务器,能够接收和响应使用访问令牌对受保护资源的请求。需要注意的是授权服务器和资源服务器可以是同一个服务器,也可以不是同一个服务器。

c.资源所有者(ResourceOwner):指拥有共享数据的人或应用。比如微信的用户或者淘宝的用户就是是资源拥有者,他们拥有的资源就是他们的数据。需要注意的是资源拥有者也可以是一个应用,不一定必须是人。

d.客户端应用(client):指请求访问存储在资源服务器的资源的应用。

如,你公司的OA系统需要使用微信进行授权登录,那么在这个过程中这四个角色分别对应
授权服务器:微信授权下发token
客户端应用:OA系统
资源服务器:微信给OA系统返回用户信息
资源所有者:微信用户
当OA系统要进行登录的时候,微信进行认证授权,微信下发token信息后,用户在OA系统中需要查看该微信用户的信息,因此OA系统携带token信息向微信发送请求,然后微信返回信息给OA系统。

1.4OAuth2.0流程

在这里插入图片描述

  • (A)客户端向从资源所有者请求授权。
  • (B)客户端收到授权许可,资源所有者给客户端颁发授权许可(比如授权码code)
  • (C)客户端与授权服务器进行身份认证并出示授权许可请求访问令牌。
  • (D)授权服务器验证客户端身份并验证授权许可,若有效则颁发访问令牌(token)。
  • (E)客户端从资源服务器请求受保护资源并出示访问令牌(token)进行身份验证。
  • (F)资源服务器验证访问令牌(token),若有效则满足该请求。

2.OAuth2.0授权模式

  • OAuth2.0有4种授权模式:
  • 授权码模式(authorization code)
  • 简化模式(implicit)
  • 密码模式(resource owner password credentials)
  • 客户端模式(client credentials)

2.1.基本参数说明

client_id:客户端应用的ID
client_secret:客户端秘钥
response_type:响应类型
grant_type:授权模式
scope:授权范围
redirect_uri:回调地址

2.1.基本流程说明

2.1.1.授权码模式

参数:client_id、client_secret、response_type、grant_type、redirect_uri

  • 第一步:携带相关基本参数重定向授权页面
    例如:当我们使用如下地址进行访问后,认证服务器将会校验client_id和client_secret,并且由于response_type(授权模式)为code(授权码模式),认证服务器校验通过后,重定向到登录页面。response_type的值为code,说明改接口最终返回的是code,而不是token。
http://<认证服务地址>?client_id=client_hutao&client_secret=secret_hutao&response_type=code&redirect_uri=http://<客户端(web)地址>

例如我们在腾讯云进行登录的时候,我们可以点击QQ授权登录
在这里插入图片描述
在这里插入图片描述

  • 第二步:进行登录
    当我们被重定向到登录页面之后,我们在这个页面进行登录。
  • 第三步:携code码返回到客户端(web)页面
    执行登录操作,验证信息通过后,这时候认证服务会携带code码重定向页面到我们上述地址中redirect_uri的地址,也就是我们客户端(web)的地址,并且会将code码也返回去。
  • 第四步:使用code码获取token
    code一般只能使用一次,使用后就会失效,grant_type就是指定认证方式为授权码模式(authorization_code)
http://<认证服务地址>??code=504dbO&grant_type=authorization_code&client_id=client_hutao&client_secret=secret_hutao

调用上述接口后,我们就会获取到token信息。

  • 第五步:携带token访问受保护资源信息
    如果token有效,那就是有效的,除非你对资源添加的角色控制。
http://<资源服务器地址>??token=XXXXXXXXXXXXXXXXXXXXXXXX

2.1.2.简化(隐式)模式

参数:client_id、response_type、redirect_uri
和授权码模式不同的是,用户进行登录操作后,直接携带token信息重定向客户端应用页面。
不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤,因此称简化模式。response_type的值为token说明该接口直接返回的信息就是token。

  • 第一步:携带相关基本参数重定向授权页面
    当然此接口由于是简化模式,因此client_secret可以不传,重定向页面时只需要校验client_id
http://<认证服务地址>?client_id=client_hutao&client_secret=secret_hutao&response_type=token&redirect_uri=http://<客户端(web)地址>
  • 第二步:进行登录
  • 第三步:携token返回到客户端(web)页面
  • 第四步:携带token访问资源信息

2.1.3.客户端(凭证)模式

和授权码以及简化模式最大区别是,不需要重定向页面,直接通过接口返回token信息,并且在这个过程中,不需要用户参与。response_type为
参数:client_id、client_secret、response_type

  • 第一步:客户端携带相关基本参数通过凭证请求登录接口,返回token信息
  • 第二步:携带token访问资源信息

2.1.4.密码模式

和客户端模式一样,直接通过接口返回token信息,但是需要在接口中传用户名和密码。
参数:client_id、client_secret、username、password

  • 第一步:客户端携带相关基本参数通过请求账号密码登录接口,返回token信息
  • 第二步:携带token访问资源信息

3.Security Oauth2快速入门

3.1.创建SpringBoot(maven)项目

如果你使用idea或者sts等工具,可以创建SpringBoot项目的时候选择相关依赖或者版本,则不用在自行添加依赖。
在这里插入图片描述

3.2.pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 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.3.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>usermanager_service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>usermanager_service</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR10</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>

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

        <!-- 热部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </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>
                <configuration>
                    <includeSystemScope>true</includeSystemScope>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

接下来我们将按照四个角色进行开始搭建并且演示。
a.授权服务器(AuthorizationServer): 在成功验证资源所有者且获得授权后颁发访问令牌给客户端的服务器,使得授权客户端应用能够访问资源拥有者所拥有的资源。

b.资源服务器(ResourceServer):托管受保护资源的服务器,能够接收和响应使用访问令牌对受保护资源的请求。需要注意的是授权服务器和资源服务器可以是同一个服务器,也可以不是同一个服务器。

c.资源所有者(ResourceOwner):指拥有共享数据的人或应用。比如微信的用户或者淘宝的用户就是是资源拥有者,他们拥有的资源就是他们的数据。需要注意的是资源拥有者也可以是一个应用,不一定必须是人。

d.客户端应用(client):指请求访问存储在资源服务器的资源的应用。

3.3.搭建授权服务器

定义授权服务器,用注解@EnableAuthorizationServer

3.3.1.客户端应用(client)信息准备

在这里我们暂时使用java对象的方式构建了一个客户端信息。注意,后续这块数据应该从数据库读取

@Data
public class ClientInfo {
	/**客户端ID*/
    private String clientId = "client_hutao";
    /**客户端秘钥*/
    private String clientSecret = "secret_hutao";
    /**授权范围*/
    private String scop = "all";
    /**token有效期*/
    private int tokenValid = 60*30*4;
    /**flush_token有效期*/
    private int flushTokenValid = 60*30*4;
    /**授权模式:授权码模式,简化模式,密码模式,客户端模式*/
    private String [] grantTypes= {"authorization_code","implicit","password","client_credentials"};
}

3.3.2.创建授权服务器

/**
 * @Description:认证服务器配置
 * 在成功验证资源所有者且获得授权后颁发访问令牌给客户端的服务器,使得授权客户端应用能够访问资源拥有者所拥有的资源
 * @author:hutao
 * @mail:hutao_2017@aliyun.com
 * @date:2021年4月21日
 */
@SpringBootConfiguration  
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter{

	@Autowired
    private AuthenticationManager authenticationManager;
	
	
	/**
	 * @Description:客户端配置:这里我们先使用内存的方式构造死数据进行入门,后续我们需要从数据库中读取客户端信息
	 * @author:hutao
	 * @mail:hutao_2017@aliyun.com
	 * @date:2021年4月21日
	 */
	@Override  
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception{  
		//使用我们构建的客户端信息,将其写入到内存中
		ClientInfo clientInfo = new ClientInfo();
		clients
        // 使用内存存储
        .inMemory()
        //标记客户端id
        .withClient(clientInfo.getClientId())
        //客户端安全码    需要注意这里存储的数据是通过BCryptPasswordEncoder进行加密后的数据
        .secret("{noop}"+clientInfo.getClientSecret())
        //为true 直接自动授权成功返回code
        .autoApprove(true)
        .redirectUris("https://www.baidu.com/") //重定向uri
        //允许授权范围
        .scopes(clientInfo.getScop())
        //token 时间秒
        .accessTokenValiditySeconds(clientInfo.getTokenValid())
        //刷新token 时间 秒
        .refreshTokenValiditySeconds(clientInfo.getFlushTokenValid())
        //允许授权类型
        .authorizedGrantTypes(clientInfo.getGrantTypes());
	}  
	
	/**
     * @Description:配置令牌访问端点:定义token的相关endpoint,以及token如何存取,以及客户端支持哪些类型的token,这里目前我们简单意见,使用内存的方式
     * @author hutao
     * @date 2021年4月21日
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 
        endpoints
        //密码模式需要配置注入的authenticationManager,后续深入在解释为什么需要注入这个
        .authenticationManager(authenticationManager)
        .tokenStore(new InMemoryTokenStore());
    }
    
    /**
	 * @Description:.令牌端点的安全约束:endpoint可以定义一些安全上的约束等
	 * @author:hutao
	 * @mail:hutao_2017@aliyun.com
	 * @date:2021年4月25日
	 */
	@Override  
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception{  
		security
		//开启/oauth/token_key验证端口认证权限访问
		.tokenKeyAccess("isAuthenticated()")
		//开启/oauth/check_token验证端口认证权限访问
		.checkTokenAccess("isAuthenticated()")
		//允许表单认证
		.allowFormAuthenticationForClients();
	}

3.4.搭建Web安全配置

3.4.1.资源所有者(ResourceOwner)信息准备

资源所有者就是用户,因此我们同样用java对象的方式构建一个用户信息。注意后续应该从数据库读取。

@Data
public class UserInfo {
	/**登录用户名*/
	private String userName = "hutao";
	/**登录密码*/
	private String passWord = "123456";
	/**用户权限*/
	private String role = "admin";
}

3.4.2.web安全访问配置


@SpringBootConfiguration  
@EnableWebSecurity
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

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

	/**
	 * @Description:认证服务器中注入的authenticationManager就是从这里来的
	 * @author:hutao
	 * @mail:hutao_2017@aliyun.com
	 * @date:2021年4月30日
	 */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    	UserInfo userInfo = new UserInfo();
        auth.inMemoryAuthentication()
                .withUser(userInfo.getUserName())
                .password("{noop}"+userInfo.getPassWord())
                .roles(userInfo.getRole());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //解决静态资源被拦截的问题
    }

	/**
	 * @Description:配置不拦截"/oauth/**",否则在申请access_token会要求先认证
	 * @author:hutao
	 * @mail:hutao_2017@aliyun.com
	 * @date:2021年4月30日
	 */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http   // 配置登录页并允许访问
                .formLogin().permitAll()
                .and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
                .and().authorizeRequests().antMatchers("/oauth/**", "/login/**", "/logout/**").permitAll()
                // 其余所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                // 关闭跨域保护;
                .and().csrf().disable();
    }
}

3.5.验证四种模式颁发token

3.5.1.参数准备

public class ClientInfo {
	/**客户端ID*/
    private String clientId = "client_hutao";
    /**客户端秘钥*/
    private String clientSecret = "secret_hutao";
    /**授权范围*/
    private String scop = "all";
    /**为true 直接自动授权成功返回code*/
    private boolean autoApprove = true;
    /**重定向uri*/
    private String redirectUris = "https://www.baidu.com/";
    /**token有效期*/
    private int tokenValid = 60*30*4;
    /**flush_token有效期*/
    private int flushTokenValid = 60*30*4;
    /**授权模式:授权码模式,简化模式,密码模式,客户端模式*/
    private String [] grantTypes= {"authorization_code","implicit","password","client_credentials"};
}

public class UserInfo {
	/**登录用户名*/
	private String userName = "hutao";
	
	/**登录密码*/
	private String passWord = "123456";
	
	/**用户权限*/
	private String role = "admin";
}


注意:由于我们这里没有使用数据库,可能某些版本会要求配置sql,因此这里我们设置不需要进行sql配置,在启动类里面配置

@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})

3.5.2.授权码模式

步骤1:获取授权码code

http://127.0.0.1:8080/oauth/authorize?client_id=client_hutao&client_secret=secret_hutao&response_type=code

请求示例:通过浏览器访上述地址,会被重定向到一个登录页面,输入用户名密码(hutao/123456),会携带code码回调到www.baidu.com页面。

前往登录页面
在这里插入图片描述
携带code回调到重定向页面
在这里插入图片描述

步骤2:授权码code换取token

现在入门阶段,我们暂时使用postman来进行调试

设置基本认证信息(Basic Auth)
username填clientId ,password填写clientSecret
在这里插入图片描述
设置Params参数

http://127.0.0.1:8080/oauth/token?code=j51uBH&grant_type=authorization_code

在这里插入图片描述
至此,通过授权码模式,获取token完毕。

除了上述这种,在认证参数中,username和password里面,client_id和client_secret,也可以像下面这种方式,直接当作请求参数传递也可以。
在这里插入图片描述

TIP:多次测试,记得清楚浏览器缓存。

3.5.3.简化(隐式)模式

简化模式与授权码模式一样,直接使用浏览器访问,会被重定向到登录页面,需要输入用户名密码,但是省去了用code换token,这种方式会直接将token直接返回。
请求示例

http://127.0.0.1:8080/oauth/authorize?client_id=client_hutao&response_type=token&scope=all

在这里插入图片描述

3.5.4.客户端(凭证)模式

请求示例

http://127.0.0.1:8080/oauth/token?client_id=client_hutao&client_secret=secret_hutao&grant_type=client_credentials

通过postman携带客户端ID,密钥,认证模式即可
在这里插入图片描述

3.5.5.密码模式

http://127.0.0.1:8080/oauth/token?client_id=client_hutao&client_secret=secret_hutao&username=hutao&password=123456&grant_type=password

在这里插入图片描述

  • 12
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring Boot Security OAuth2是基于Spring SecurityOAuth2的框架,用于实现授权服务器和资源服务器。JWT令牌是一个基于JSON的开放标准,用于在各方之间安全地传输信息。 在Spring Boot Security OAuth2中实现支持JWT令牌的授权服务器,可以按照以下步骤进行: 1. 添加依赖:在项目的pom.xml文件中添加Spring Security OAuth2和JWT的相关依赖。 2. 配置授权服务器:在Spring Boot应用程序的配置文件中,配置授权服务器的基本设置,包括端点URL、客户端信息、用户认证信息等。 3. 配置JWT令牌:配置JWT令牌的签名密钥和过期时间等信息。可以使用开源库如jjwt来生成和验证JWT令牌。 4. 创建自定义的认证提供程序:实现自定义的认证提供程序来支持JWT令牌的认证机制。在认证提供程序中,可以使用JWT令牌解析并验证请求中的令牌信息。 5. 创建自定义的用户详细信息服务:实现自定义的用户详细信息服务,用于从数据库或其他存储中获取用户的详细信息。在用户详细信息服务中,可以根据JWT令牌中的信息获取用户信息。 6. 配置授权服务器的访问规则:配置授权服务器的访问规则,包括允许或禁止特定角色或权限的访问。 7. 测试访问授权服务器:使用客户端应用程序发送请求到授权服务器的端点,获取JWT令牌并验证其有效性。 通过以上步骤,可以实现一个支持JWT令牌的授权服务器。该服务器可以提供为客户端应用程序颁发和验证JWT令牌的功能,以实现安全并可靠的用户认证和授权控制。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值