Spring Security Oauth2学习(一)
一、用户认证需求分析
1.用户认证和授权
什么是用户身份认证?
用户身份认证就是用户去访问系统资源时系统要求验证用户的身份信息,身份验证合法才可以继续访问。
常见的认证方式:用户账户登录、指纹识别、人脸识别登录等。
什么是用户授权?
用户认证通过以后去访问系统资源,系统会判断用户是否拥有访问系统资源的权限,只允许访问有权限的资源,没有权限的资源将无法访问,这个过程叫用户授权。
2.单点登录
引用百度百科:单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。
SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
下图是SSO的示意图,用户登录学成网一次即可访问多个系统。
3.第三方认证需求
什么是第三方认证(跨平台认证)?
当需要访问第三方系统的资源时需要首先通过第三方系统的认证(例如:微信认证),由第三方系统对用户认证通过,并授权资源的访问权限。
二、Oauth2认证
第三方认证技术方案最主要的就是解决认证协议的通用标准问题,因为要实现跨系统认证,各系统之间要遵循一定的接口协议。
OAUTH协议为用户资源授权提供了一个安全的、开发而又简易的标准。同时,任何第三方都可以使用OAUTH认证服务,任何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开发的。业界提供了OAUTH的多种实现如PHP、JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间,因而OAUTH是简易的。互联网很多服务如Open API,很多大公司Google,Yahoo,Microsoft等都提供了OAUTH认证服务,这些都足以说明OAUTH标准逐渐成为开放资源授权的标准。
参考:https://baike.baidu.com/item/oAuth/7153134?fr=aladdin
Oauth2.0认证流程如下:
引自Oauth2.0协议rfc6749 https://tools.ietf.org/html/rfc6749
Oauth2拥有以下几个角色:
1.客户端
本身不存储资源,需要通过资源拥有者的授权去请求资源服务器的资源,比如:Android客户端、在线Web客户端(浏览器端)、微信客户端等。
2.资源拥有者
通常为用户,也可以是应用程序,即该资源的拥有者。
3.授权服务器(也称认证服务器)
用来对资源拥有的身份进行认证、对访问资源进行授权。客户端想要访问资源需要通过认证服务器由资源拥有者授权后方可访问。
4.资源服务器
存储资源的服务器,比如用户管理服务器存储了用户信息,微信资源服务器存储了微信用户信息,商品管理服务器存储了商品信息等。客户端最终访问资源服务器获取资源信息。
Oauth2授权模式
Oauth2有以下授权模式:
授权码模式(Authorization Code) 隐式授权模式(Implicit) 密码模式(Resource Owner Password
Credentials) 客户端模式(Client Credentials)
其中授权码模式和密码模式应用较多。我主要介绍一下这两张授权模式。
1.Oauth2授权码认证过程(主要应用于第三方认证)
下边分析一个Oauth2认证的例子,黑马程序员网站使用微信认证的过程:
1.客户端请求第三方授权
用户进入黑马程序的登录页面,点击微信的图标以微信账号登录系统,用户是自己在微信里信息的资源拥有者。
2、资源拥有者同意给客户端授权
资源拥有者扫描二维码表示资源拥有者同意给客户端授权,微信会对资源拥有者的身份进行验证, 验证通过后,微信会询问用户是否给授权黑马程序员访问自己的微信数据,用户点击“确认登录”表示同意授权,微信认证服务器会颁发一个授权码,并重定向到黑马程序员的网站。
3、客户端获取到授权码,请求认证服务器申请令牌
此过程用户看不到,客户端应用程序请求认证服务器,请求携带授权码。
4、认证服务器向客户端响应令牌
认证服务器验证了客户端请求的授权码,如果合法则给客户端颁发令牌,令牌是客户端访问资源的通行证。
此交互过程用户看不到,当客户端拿到令牌后,用户在黑马程序员看到已经登录成功。
5、客户端请求资源服务器的资源
客户端携带令牌访问资源服务器的资源。
黑马程序员网站携带令牌请求访问微信服务器获取用户的基本信息。
6、资源服务器返回受保护资源
资源服务器校验令牌的合法性,如果合法则向用户响应资源信息内容。
注意:资源服务器和认证服务器可以是一个服务也可以分开的服务,如果是分开的服务资源服务器通常要请求认证服务器来校验令牌的合法性。
上边例举的黑马程序员网站使用微信认证的过程就是授权码模式,流程如下:
1、客户端请求第三方认证 2、用户(资源拥有者同意给客户端授权)3、客户端获取授权码,请求认证服务器申请令牌 4、认证服务器向客户端响应令牌 5、客户端请求资源服务器的资源,资源服务效验令牌合法性,完成授权 6、资源服务器返回受保护资源
申请授权码
请求认证服务获取授权码:
Get请求:
localhost:40400/auth/oauth/authorize? client_id=XcWebApp&response_type=code&scop=app&redirect_uri=http://localhost
参数列表如下:
client_id:客户端id,和授权配置类中设置的客户端id一致。
response_type:授权码模式固定为code
scop:客户端范围,和授权配置类中设置的scop一致。
redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数(授权码)。
首先跳转到登录页面:
输入账号密码,点击登录。
Spring Security接收到请求会调用UserDetailsService接口的loadUserByUsername方法查询用户正确的密码。
账号密码验证通过,接下来进入授权页面:
点击“同意”。
接下来返回授权码:
认证服务携带授权码跳转redirect_uri
申请令牌
拿到授权码后,申请令牌。
Post请求:
http://localhost:40400/auth/oauth/token
参数如下:
grant_type:授权类型,填写authorization_code,表示授权码模式
code:授权码,就是刚刚获取的授权码,注意:授权码只使用一次就无效了,需要重新申请。
redirect_uri:申请授权码时的跳转url,一定和申请授权码时用的redirect_uri一致。
此链接需要使用 http Basic认证。
什么是http Basic认证?
http协议定义的一种认证方式,将客户端id和客户端密码按照“客户端ID:客户端密码”的格式拼接,并用base64编
码,放在header中请求服务端,一个例子:
Authorization: Basic WGNXZWJBcHA6WGNXZWJBcHA=
WGNXZWJBcHA6WGNXZWJBcHA= 是用户名:密码的base64编码。
认证失败服务端返回 401 Unauthorized
以上测试使用postman完成:
http basic认证:
右边的账号密码:是spring Security自带的表 oauth_client_details表里存的,后面细讲该表。
客户端Id和客户端密码会匹配数据库oauth_client_details表中的客户端id及客户端密码。
Post请求参数:点击Body,填写参数
点击发送:
申请令牌成功:
access_token:访问令牌,携带此令牌访问资源
token_type:有MAC Token与Bearer Token两种类型,两种的校验算法不同,RFC 6750建议Oauth2采用 Bearer Token(http://www.rfcreader.com/#rfc6750)。
refresh_token:刷新令牌,使用此令牌可以延长访问令牌的过期时间。
expires_in:过期时间,单位为秒。
scope:范围,与定义的客户端范围一致。
2. Oauth2密码模式授权
密码模式(Resource Owner Password Credentials)与授权码模式的区别是申请令牌不再使用授权码,而是直接通过用户名和密码即可申请令牌。
测试如下:
Post请求:
http://localhost:40400/auth/oauth/token
参数:
grant_type:密码模式授权填写password
username:账号
password:密码
并且此链接需要使用 http Basic认证。
上边参数使用x-www-form-urlencoded方式传输,使用postman测试如下:
注意:当令牌没有过期时同一个用户再次申请令牌则不再颁发新令牌。
校验令牌
Spring Security Oauth2提供校验令牌的端点,如下:
Get:
http://localhost:40400/auth/oauth/check_token?token=
参数:
token:令牌
使用postman测试如下:
结果如下:
{ "companyId": null, "userpic": null, "user_name": "mrt", "scope": [ "app" ], "name": null, "utype": null, "id": null, "exp": 1531254828, "jti": "6a00f227‐4c30‐47dc‐a959‐c0c147806462", "client_id": "XcWebApp" }
exp:过期时间,long类型,距离1970年的秒数(new Date().getTime()可得到当前时间距离1970年的毫秒数)。
user_name: 用户名
client_id:客户端Id,在oauth_client_details中配置
scope:客户端范围,在oauth_client_details表中配置
jti:与令牌对应的唯一标识
companyId、userpic、name、utype、id:这些字段是本认证服务在Spring Security基础上扩展的用户身份信息
刷新令牌
刷新令牌是当令牌快过期时重新生成一个令牌,它于授权码授权和密码授权生成令牌不同,刷新令牌不需要授权码也不需要账号和密码,只需要一个刷新令牌、客户端id和客户端密码。
测试如下:
Post请求:
http://localhost:40400/auth/oauth/token
参数:
grant_type: 固定为 refresh_token
refresh_token:刷新令牌(注意不是access_token,而是refresh_token)
刷新令牌成功,会重新生成新的访问令牌和刷新令牌,令牌的有效期也比旧令牌长。
刷新令牌通常是在令牌快过期时进行刷新。
3.资源服务授权
前面两节都是获取令牌的主要流程和相关请求过程。资源服务拥有要访问的受保护资源,客户端获取令牌以后会携带令牌去访问资源,如下图:
上图的业务流程如下:
1、客户端请求认证服务申请令牌
2、认证服务生成令牌
认证服务采用非对称加密算法,使用私钥生成令牌。
3、客户端携带令牌访问资源服务
客户端在Http header 中添加: Authorization:Bearer 令牌。
4、资源服务请求认证服务校验令牌的有效性
资源服务接收到令牌,使用公钥校验令牌的合法性。
5、令牌有效,资源服务向客户端响应资源信息
资源服务授权配置
基本上所有微服务都是资源服务,这里我们在课程管理服务上配置授权控制,当配置了授权控制后如要访问课程信息则必须提供令牌。
1.配置公钥
认证服务生成令牌采用非对称加密算法,认证服务采用私钥加密生成令牌,对外向资源服务提供公钥,资源服务使用公钥 来校验令牌的合法性。
将公钥拷贝到 publickey.txt文件中,将此文件拷贝到资源服务工程的classpath下
2、添加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring‐cloud‐starter‐oauth2</artifactId> </dependency>
3.创建ResourceServerConfig类:
@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)//激活方法上的PreAuthorize注解 public class ResourceServerConfig extends ResourceServerConfigurerAdapter { //公钥 private static final String PUBLIC_KEY = "publickey.txt"; //定义JwtTokenStore,使用jwt令牌 @Bean public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) { return new JwtTokenStore(jwtAccessTokenConverter); } //定义JJwtAccessTokenConverter,使用jwt令牌 @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setVerifierKey(getPubKey()); return converter; } /** * 获取非对称加密公钥 Key * @return 公钥 Key */ private String getPubKey() { Resource resource = new ClassPathResource(PUBLIC_KEY); try { InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream()); BufferedReader br = new BufferedReader(inputStreamReader); return br.lines().collect(Collectors.joining("\n")); } catch (IOException ioe) { return null; } } //Http安全配置,对每个到达系统的http请求链接进行校验 @Override public void configure(HttpSecurity http) throws Exception { //所有请求必须认证通过 http.authorizeRequests().anyRequest().authenticated(); } }
资源服务授权测试
以查询获取图片为例:
get
http://localhost:31200/course/coursepic/list/4028e58161bd3b380161bd3bcd2f0000
请求时没有携带令牌则报错:
{ "error": "unauthorized", "error_description": "Full authentication is required to access this resource" }
请求时携带令牌:
在http header中添加 Authorization: Bearer 令牌
当输入错误的令牌也无法正常访问资源。
4.Oauth2相关表
以“oauth_”开头的表都是spring Security 自带的表
本项目中spring Security 主要使用oauth_client_details表:
client_id:客户端id
resource_ids:资源id(暂时不用)
client_secret:客户端密码
scope:范围
access_token_validity:访问token的有效期(秒)
refresh_token_validity:刷新token的有效期(秒)
authorized_grant_type:授权类型,authorization_code,password,refresh_token,client_credentials
CREATE TABLE `oauth_client_details` ( `client_id` VARCHAR ( 48 ) NOT NULL COMMENT '客户端ID', `resource_ids` VARCHAR ( 256 ) DEFAULT NULL COMMENT '资源id', `client_secret` VARCHAR ( 256 ) DEFAULT NULL COMMENT '客户端密码', `scope` VARCHAR ( 256 ) DEFAULT NULL, `authorized_grant_types` VARCHAR ( 256 ) DEFAULT NULL COMMENT '授权类型,authorization_code,password,refresh_token,client_credentials', `web_server_redirect_uri` VARCHAR ( 256 ) DEFAULT NULL, `authorities` VARCHAR ( 256 ) DEFAULT NULL, `access_token_validity` INT ( 11 ) DEFAULT NULL COMMENT '访问token的有效期(秒)', `refresh_token_validity` INT ( 11 ) DEFAULT NULL COMMENT '刷新token的有效期(秒)', `additional_information` VARCHAR ( 4096 ) DEFAULT NULL, `autoapprove` VARCHAR ( 256 ) DEFAULT NULL, PRIMARY KEY ( `client_id` ) ) ENGINE = INNODB DEFAULT CHARSET = utf8;
有兴趣的老爷,可以关注我的公众号【一起收破烂】,回复【006】获取2021最新java面试资料以及简历模型120套哦~