协议中的角色
OAUTH协议
为用户资源的授权提供了一个安全开放而又简易的标准,任何第三方都可以使用OAUTH认证服务,任何服务提供商都可以实现自身的OAUTH认证服务
OAuth协议目前发展到2.0版本并得到广泛应用(1.0版本过于复杂),微信扫码认证
就是一种基于OAuth2协议实现的第三方认证方式
请求微信服务端进行认证
,认证通过后微信会向用户返回等待的授权页面即是否确认登陆授权码
给目标网站颁发令牌
,微信认证服务器收到请求后向目标网站响应令牌(此交互过程用户看不到)协议的应用
OAuth2是一个标准的开放的授权协议,应用程序可以根据自己的需求去使用
基于OAuth2协议访问微信服务端中的用户信息
基于OAuth2协议访问学成在线认证服务中的用户信息
前端页面即客户端
访问学成在线微服务的资源时也可以基于OAuth2协议Spring Security支持OAuth2认证
,OAuth2提供授权码模式(微信扫描)、密码模式、简化模式(了解)、客户端模式(了解 )
等四种授权模式
授权码模式
: 适合客户端和第三方系统的认证服务在非同一个系统的情况,所以本项目采用授权码模式完成微信扫码认证密码模式
: 适用于认证服务系统是我们自己开发的情况下,所以本项目采用密码模式作为前端请求微服务的认证方式OAuth2中不同的授权模式对应不同的获取令牌的方式
,最终目的都是从获取认证服务颁发的令牌,然后通过令牌去获取资源
配置步骤
第一步: 在xuecheng-plus-auth
工程的config
包下配置基于OAuth2.0的认证授权服务,包含AuthorizationServer(颁发授权码)
和TokenConfig(颁发令牌)
第二步: 创建AuthorizationServer
配置类继承AuthorizationServerConfigurerAdapter
并重写方法,然后使用@EnableAuthorizationServer注解
标识
类 | 功能 |
---|---|
AuthorizationServerSecurityConfigurer | 用来配置令牌端点的安全约束 |
ClientDetailsServiceConfigurer | 用来配置客户端详情服务,如客户端的Id和密钥(明文或BCrypt格式格式) 并不是随便一个客户端都可以接入到系统的认证服务中,认证服务提供商会给批准接入的客户端一个身份用于接入时凭据,包含客户端的标识和秘钥 我们想要接入微信的认证服务就需要一个凭据 |
AuthorizationServerEndpointsConfigurer | 用来配置令牌的访问端点和令牌服务 |
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Resource(name="authorizationServerTokenServicesCustom")
private AuthorizationServerTokenServices authorizationServerTokenServices;
@Autowired
private AuthenticationManager authenticationManager;
// 客户端详情服务
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory()// 使用inmemory存储客户端的详情信息
.withClient("XcWebApp")// 客户端Id
// 客户端密钥基于明文的方式
.secret("XcWebApp")
// 客户端密钥基于BCrypt格式
.secret(new BCryptPasswordEncoder().encode("XcWebApp"))
.resourceIds("xuecheng-plus") //资源列表
// 认证服务允许的授权类型即客户端申请令牌的方式
.authorizedGrantTypes("authorization_code", "password","client_credentials","implicit","refresh_token") // 允许的授权范围
.scopes("all")
// false表示跳转到授权页面
.autoApprove(false)
// 客户端接收授权码时重定向的地址
.redirectUris("http://www.51xuecheng.cn");
}
// 令牌端点的访问配置
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(authenticationManager)//认证管理器
.tokenServices(authorizationServerTokenServices)//令牌管理服务
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
}
// 令牌端点的安全配置
@Override
public void configure(AuthorizationServerSecurityConfigurer security){
security
.tokenKeyAccess("permitAll()") //oauth/token_key是公开
.checkTokenAccess("permitAll()")//oauth/check_token公开
.allowFormAuthenticationForClients();//表单认证(申请令牌)
}
}
第三步:创建TokenConfig
配置类配置令牌的相关策略,使用InMemoryTokenStore
在内存存储令牌
@Configuration
public class TokenConfig {
@Autowired
TokenStore tokenStore;
@Bean
public TokenStore tokenStore() {
// 在内存中存储令牌,这种令牌是普通令牌
return new InMemoryTokenStore();
}
@Bean(name = "authorizationServerTokenServicesCustom")
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service = new DefaultTokenServices();
service.setSupportRefreshToken(true);//支持刷新令牌
service.setTokenStore(tokenStore);//令牌存储策略
service.setAccessTokenValiditySeconds(7200); //令牌默认有效期2小时
service.setRefreshTokenValiditySeconds(259200); //刷新令牌默认有效期3天
return service;
}
}
第四步: 在config/WebSecurityConfig
中配置负责认证管理的Bean
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
···
}
测试授权码模式
第一步: 重启认证服务,先登陆账号
然后发送Get请求获取授权码
,认证服务颁发的授权码使用一次后就无效了,需要重新申请
,请求参数取决于AuthorizationServer
配置类中配置的信息
使用授权码去获取令牌
,所以首先要获取授权码,授权码的获取需要资源拥有者亲自授权同意才可以获取获取授权码
: http://localhost:63070/auth/oauth/authorize?client_id=XcWebApp&response_type=code&scope=all&redirect_uri=http://localhost/请求参数 | 描述 |
---|---|
client_id | 客户端准入标志 |
response_type | 对于授权码模式固定为code |
scope | 客户端权限 |
redirect_uri | 当授权码申请成功后门会跳转到此地址并在后面带上code=授权码 ,如http://localhost/?code=H7J61Z |
第二步: 携带认证服务端响应的授权码发送Post请求申请令牌
,相关请求参数取决于AuthorizationServer
中配置的客户端详情服务
申请令牌
:http://localhost:63070/auth/oauth/token?client_id=XcWebApp&client_secret=XcWebApp&grant_type=authorization_code&code=H7J61Z
&redirect_uri=http://localhost/请求参数 | 描述 |
---|---|
client_id | 客户端准入标识 |
client_secret | 客户端秘钥 |
grant_type | 授权类型,authorization_code表示授权码模式,需要在AuthorizationServer中配置 |
code | 认证服务端响应的授权码 |
redirect_uri | 当令牌申请成功后门会跳转到此地址,和申请授权码时使用的redirect_uri一致 |
第三步: 查看认证服务端响应的令牌
响应参数 | 描述 |
---|---|
access_token | 令牌用于访问资源使用 |
token_type | bearer 是在RFC6750中定义的一种token类型,在携带令牌访问资源时需要在head中加入bearer 令牌内容(空格分开) |
refresh_token | 当令牌快过期时使用刷新令牌,可以再次生成令牌 |
expires_in | 过期时间 |
scope | 令牌的权限范围,服务端可以根据令牌的权限范围去对令牌授权 |
// 携带授权码获取令牌
POST {{auth_host}}/auth/oauth/token?client_id=XcWebApp&client_secret=XcWebApp&grant_type=authorization_code&code=H7J61Z&redirect_uri=http://localhost/
// 申请令牌成功如下所示
{
"access_token": "c75d121f-7430-4cf9-9ff6-eb25e5c01ca0",
"token_type": "bearer",
"refresh_token": "b950149e-40e8-47f3-9f1d-1c6df74dd69f",
"expires_in": 7199,
"scope": "all"
}
测试密码模式
授权码模式需要借助浏览器供用户亲自授权, 而密码模式较简单不用借助浏览器,但是却直接将用户的敏感信息泄露给了客户端
第一步: 直接发送Post请求携带客户端的Id和密钥
以及用户的账号和密码
等请求参数访问xuecheng-plus-auth
工程的认证服务获取令牌
POST {{auth_host}}/auth/oauth/token?client_id=XcWebApp&client_secret=XcWebApp&grant_type=password&username=Kyle&password=123
请求参数 | |
---|---|
client_id | 客户端准入标识 |
client_secret | 客户端秘钥 |
grant_type | 授权类型,填写password标识密码模式 |
username | 资源拥有者用户名 |
password | 资源拥有者密码 |
第二步: 查看服务端响应的令牌
{
"access_token": "c75d121f-7430-4cf9-9ff6-eb25e5c01ca0",
"token_type": "bearer",
"refresh_token": "b950149e-40e8-47f3-9f1d-1c6df74dd69f",
"expires_in": 4687,
"scope": "all"
}