SpringSecurityOAuth2Demo——授权码模式(Authorization。。。
本⽂可以转载,但请注明出处wwwblogs/hellxz/p/oauth2_oauthcode_pattern.html
写在前边
在⽂章中我们谈到OAuth 2.0的概念与流程,这⾥我准备分别记⼀记这⼏种授权模式的demo,⼀⽅⾯为⾃⼰的最近的学习做个总结,另⼀⽅⾯做下知识输出,如果⽂中有错误的地⽅,请评论指正,在此不胜感激
受众前提
阅读本⽂,默认读者已经过Spring Security有⼀定的了解,对OAuth2流程有⼀定了解
本⽂⽬标
带领读者对Spring Security OAuth2框架的授权码模式有⼀个⽐较直观的概念,能使⽤框架搭建授权码模式授权服务器与资源服务器(分离版本)
授权码模式流程回顾
授权码模式要求:⽤户登录并对第三⽅应⽤(客户端)进⾏授权,出⽰授权码交给客户端,客户端凭授权
码换取access_token(访问凭证)
此模式要求授权服务器与⽤户直接交互,在此过程中,第三⽅应⽤是⽆法获取到⽤户输⼊的密码等信息的,这个模式也是OAuth 2.0中最安全的⼀个
Demo基本结构
这⾥主要关注authorization-code-authorization-server与authorization-code-resource-server这两个模块
本⽂以及后续⽂章的demo均放在GitHub上,欢迎⼤家Star & Fork,源码地址:
authorization-code-client-resttemplate-jdbc这个项⽬是⽤来测试⾮OAuth2服务使⽤RestTemplate与JdbcTemplate对接OAuth2授权服务的,流程这⾥不讲,有兴趣可以debug看看,可能会让您对整个流程会有更清晰的感受
Maven依赖
<!--Spring Security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--Spring Boot Starter Web 所有demo均使⽤web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security OAuth2 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>${spring-security-oauth2.version}</version>
</dependency>
搭建授权服务器(Authorization Server)
⽂中服务器均使⽤demo级别配置,请勿直接使⽤到⽣产环境
授权服务器结构主体如下:
启动类⾃不多说,先说下SecurityConfig
package com.github.fig;
import t.annotation.Bean;
import t.annotation.Configuration;
import org.fig.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.fig.annotation.web.builders.HttpSecurity;
import org.fig.figuration.EnableWebSecurity;
import org.fig.figuration.WebSecurityConfigurerAdapter;
import org.pto.bcrypt.BCryptPasswordEncoder;
import org.pto.password.PasswordEncoder;
import java.util.Collections;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter: off
auth.inMemoryAuthentication()
.withUser("hellxz")
.password(passwordEncoder().encode("xyz"))
.
ptyList());
spring启动流程面试回答// @formatter: on
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated() //所有请求都需要通过认证
.and()
.httpBasic() //Basic登录
.and()
.csrf().disable(); //关跨域保护
}
}
通过@Configuration和@EnableWebSecurity开启Spring Security配置,继承WebSecurityConfigurerAdapter的⽅法,实现个性化配置,这⾥我们使⽤内存保存⼀个名为hellxz、密码为xyz的⽤户,与授权服务器交互的⽤户就是他了
除了配置⽤户,我们需要对服务的资源进⾏保护,这⾥将所有的请求都要求通过认证才可以访问,⽤户登录需要使⽤httpBasic形式(就是那种⽹页弹个窗要求登录的那种 )Spring Security 5.x版本后,要求显⽰声明使⽤的密码器,就是PasswordEncoder了,常⽤BCryptPasswordEncoder,简单的可以认为它是使⽤时间戳和盐进⾏加密的⼀种算法,同⼀个密码被加密后也不会相同
接着看看授权服务器的配置,画重点
package com.github.fig;
import org.springframework.beans.factory.annotation.Autowired;
import t.annotation.Configuration;
import org.pto.password.PasswordEncoder;
import org.springframework.figurers.ClientDetailsServiceConfigurer;
import org.springframework.fig.figuration.AuthorizationServerConfigurerAdapter;
import org.springframework.fig.figuration.EnableAuthorizationServer;
import org.springframework.fig.figurers.AuthorizationServerSecurityConfigurer;
//授权服务器配置
@Configuration
@EnableAuthorizationServer //开启授权服务
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
//允许表单提交
security.allowFormAuthenticationForClients()
.checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// @formatter: off
clients.inMemory()
.withClient("client-a") //client端唯⼀标识
.de("client-a-secret")) //客户端的密码,这⾥的密码应该是加密后的
.authorizedGrantTypes("authorization_code") //授权模式标识
.scopes("read_user_info") //作⽤域
.resourceIds("resource1") //资源id
.redirectUris("localhost:9001/callback"); //回调地址
// @formatter: on
}
}
1.通过@Configuration和EnableAuthorizationServer开启授权服务器配置,通过重写AuthorizationServerConfigurerAdapter的⽅法来完成⾃定义授权服务器
2.OAuth2授权码模式中,要求不仅仅⽤户需要登录,还要求客户端也需要登录,这⾥就需要在configure(ClientDetailsServiceConfigurer clients)这个⽅法中配置客户端(第三⽅应⽤)的登录信息,
withClient中配置的是客户端id(client_id)
secret为客户端的密码,要求使⽤加密器进⾏加密
授权码的authorizedGrantTypes必须配置有"authorization_code"(授权码模式),这⾥是可以同时⽀持多种授权模式的,为了简单只写⼀个
scopes,请求资源作⽤域,⽤于限制客户端与⽤户⽆法访问没有作⽤域的资源
resourceIds,可选,资源id,可以对应⼀个资源服务器,个⼈理解为某个资源服务器的所有资源标识
redirectUris,回调地址,有两个作⽤:1.回调客户端地址,返回授权码; 2.校验是否是同⼀个客户端
redirectUris校验是否同⼀个客户端这个,可能说的不是很准确,说下⼤体流程,我们在授权服务器上配置了这个回调地址,授权服务器在⽤户授权成功后,返回授权码的地址就是它,另外我们后续申请token时,也需要传递这个回调地址,所以我的理解是校验是否是同⼀客户端发来的第⼆次请求(换token时)
4.application.properties
这⾥我只配置了server.port=8080
这样我们就配置了相当简易的授权服务器,启动测试下
获取授权码的流程,⼀般是由客户端使⽤⾃⼰的client_id与密码+response_type=code拼接url,让浏览器跳转完成的,⽤户的登录与授权过程都需要在浏览器中完成,启动项⽬后访问下列url
登录⽤户/密码: hellxz/xyz ,选择Approve表⽰接受授权,Deny反之,如下动图所⽰
最后我们得到了回调地址localhost:9001/callback?code=2e6450
这⾥的code就是授权码,接下来我们使⽤授权码进⾏换取token
POST请求,
BasicAuth:这⾥填的是客户端配置的client_id和client_secret的值,相当于curl --user client_id:client_secret,配置后会在Header中添加Authorization:Basic Y2xpZW50LWE6Y2xpZW50LWEtc2VjcmV0,Basic空格后的是client_id:client_secret具体值被Base64后得到的值
请求参数列表:
code=授权码
grant_type=authorization_code
redirect_uri=回调url ,要与配置处和获取授权码处相同
scope=作⽤域
最后我们获得了授权服务的响应,包含token的json
{
"access_token": "99435e13-f9fe-438a-a94e-3b00d549b329", //访问token
"token_type": "bearer", //token类型,使⽤时需要拼接在token前并在token前加空格
"expires_in": 43199, //过期时间
"scope": "read_user_info" //作⽤域
}
在access_token未过期之前,同⼀个⽤户名使⽤同⼀个客户端访问都会是同⼀个access_token
授权服务器先放在这⾥,不要关服,接下来搭建资源服务器
搭建资源服务器(Resource Server)
资源服务器结构
⼊⼝类不多说,先搭建资源服务器主要配置,这⾥直接使⽤ResourceConfig进⾏配置
package com.github.fig;
import t.annotation.Bean;
import t.annotation.Configuration;
import t.annotation.Primary;
import org.fig.annotation.web.builders.HttpSecurity;
import org.fig.http.SessionCreationPolicy;
import org.pto.bcrypt.BCryptPasswordEncoder;
import org.pto.password.PasswordEncoder;
import org.springframework.fig.figuration.EnableResourceServer;
import org.springframework.fig.figuration.ResourceServerConfigurerAdapter;
import org.springframework.fig.figurers.ResourceServerSecurityConfigurer;
import org.springframework.security.ken.RemoteTokenServices;
@Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Primary
@Bean
public RemoteTokenServices remoteTokenServices() {
final RemoteTokenServices tokenServices = new RemoteTokenServices();
//设置授权服务器check_token端点完整地址
tokenServices.setCheckTokenEndpointUrl("localhost:8080/oauth/check_token");
//设置客户端id与secret,注意:client_secret值不能使⽤passwordEncoder加密!
tokenServices.setClientId("client-a");
tokenServices.setClientSecret("client-a-secret");
return tokenServices;
}
@Override
public void configure(HttpSecurity http) throws Exception {
//设置创建session策略
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
//@formatter:off
//所有请求必须授权
http.authorizeRequests()
.anyRequest().authenticated();
//@formatter:on
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
}
}
1.通过@Configuration和@EnableResourceServer这两个注解标识服务是⼀个资源服务器,重写ResourceServerConfigurerAdapter来实现⾃定义授权服务器
2.配置configure(HttpSecurity http)⽅法,这⾥可以代替Spring Security同名⽅法配置,开启所有请求需要授权才可访问
3.配置资源相关设置configure(ResourceServerSecurityConfigurer resources),这⾥只设置resourceId
后续的使⽤redis校验token也在这⾥设置
4.校验token的配置,这⾥使⽤了远程调⽤授权服务器帮忙校验token的⽅式,只需要显⽰注⼊RemoteTokenServices remoteTokenServices()的Bean,就可以调⽤授权服务器的/oauth/check_token端点,设置客户端配置的值,详见注释
这样⼀来我们就配置好了资源服务器,当然光有配置是不够的,我们搞⼀个资源接⼝做测试⽤
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论