用户认证与授权:
用户身份认证:用户去访问系统资源时系统要求验证用户的身份信息,身份合法方可继续访问。常见的用户身份认证表现形式有:用户名密码登录,指纹打卡等方式。
用户授权:用户认证通过后去访问系统的资源,系统会判断用户是否拥有访问资源的权限,只允许访问有权限的系统资源,没有权限的资源将无法访问,这个过程叫用户授权。
单点登录(Single Sign On):本项目包括多个子项目,为了提高用户体验性需要实现用户只认证一次便可以在多个拥有访问权限的系统中访问,这个功能叫做单点登录。
第三方认证:
当需要访问第三方系统资源时需要首先通过第三方系统的认证(例如:微信认证),由第三方系统对用户认证通过,并授权资源的访问权限。
微信扫码登录:微信不属于本系统,本系统并没有存储微信用户的账号、密码等信息,本系统如果要获取该用户的基本信息则需要首先通过微信的认证系统(微信认证)进行认证,微信认证通过后本系统便可获取该微信用户的基本信息,从而在本系统将该微信用户的头像、昵称等信息显示出来,该用户便不用在本系统注册即可直接学习。
单点登录的特点:
1、认证系统为独立的系统
2、各子系统通过Http或其它协议与认证系统通信,完成用户认证
3、用户身份信息存储在Redis集群
Oauth2认证:
第三方认证技术方案最主要是解决认证协议的通用标准问题,因为要实现跨系统认证,各系统之间要遵循一定的接口协议。
OAUTH协议为用户资源的授权提供了一个安全、开放而又简易的标准。同时任何第三方都可以使用OAUTH认证服务,任何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的,OAUTH标准逐渐成为开放资源授权的标准。
微信认证流程:
1、用户点击微信登录
2、页面由后端生成微信二维码
3、用户扫描二维码授权登录
4、微信后台接收到授权码然后认证
5、后台接收到微信回调函数返回的用户信息
6、验证该用户如果第一次登陆存储该用户信息,如果不是第一次登陆请求认证服务器给予权限
7、后台处理用户信息将信息展示到页面
后端工程搭建
1、创建xc-service-ucenter-auth
工程
pom中导入:
<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>
2、创建xc_user表
以“oauth_”开头的表都是spring Security 自带的表。
本项目中spring Security 主要使用oauth_client_details表:
Oauth2授权模式:
授权码模式(Authorization Code) 、隐式授权模式(Implicit) 、密码模式(Resource Owner Password Credentials) 、客户端模式(Client Credentials)
其中授权码模式和密码模式应用较多,本小节介绍授权码模式
3、授权码授权流程
1、客户端请求第三方授权
2、用户(资源拥有者)同意给客户端授权
3、客户端获取到授权码,请求认证服务器申请令牌
4、认证服务器向客户端响应令牌
5、客户端请求资源服务器的资源,资源服务校验令牌合法性,完成授权
6、资源服务器返回受保护资源
启动工程,浏览器输入如下地址,请求认证服务获取授权码:
localhost:40400/auth/oauth/authorize?
client_id=XcWebApp&response_type=code&scop=app&redirect_uri=http://localhost
参数列表如下:
client_id:客户端id,和授权配置类oauth_client_details中设置的客户端id一致
response_type:授权码模式固定为code,密码模式为password
scop:客户端范围,和授权配置类中设置的scop一致
redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数(授权码)
首页跳转到如下页面:
输入用户名和密码后跳转http://localhost
路径,网址最右侧的code即为授权码。
用户名和密码都是:XcWebApp
点击Authorize授权登录后跳转到首页:
拿到授权码:JyJ40q接着申请令牌
申请令牌
利用postman请求:
Post请求:http://localhost:40400/auth/oauth/token
此链接需要使用 http Basic认证。
响应数据:
access_token:访问令牌
token_type:令牌类型
refresh_token:刷新令牌
expires_in:令牌过期时间
scope:作用范围
jti:通常存储到session中
资源服务授权配置
资源服务拥有要访问的受保护资源,客户端携带令牌访问资源服务,如果令牌合法则可成功访问资源服务中的资源,如下图:
1、配置公钥
启动course工程,输入如下内容:http://localhost:31200/course/coursepic/list/297e7c7c62b888f00162b8a7dec20000
无法访问,如下图所示:
需要放行后才能访问,下面有解决方法。
正常显示:
认证服务生成令牌采用非对称加密算法,认证服务采用私钥加密生成令牌,对外向资源服务提供公钥,资源服务使用公钥来校验令牌的合法性。
微
服
务
接
入
认
证
,
需
要
如
下
操
作
:
\color{#FF0000}{微服务接入认证,需要如下操作:}
微服务接入认证,需要如下操作:
将公钥拷贝到 publickey.txt文件中,将此文件拷贝到课程服务工程的resource下
2、pom中添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
3、在config包下创建ResourceServerConfig类
4、测试课程图片携带令牌查询
get:http://localhost:31200/course/coursepic/list/297e7c7c62b888f00162b8a7dec20000
请求时没有携带令牌则报错,如上图
请求时携带令牌:
在http header中添加 Authorization: Bearer+空格+令牌
当输入错误的令牌也无法正常访问资源。
当课程管理加了授权之后再访问swagger-ui则报错:
针对swagger-ui的请求路径进行放行,修改授权配置类ResourceServerConfig的configure方法:
//Http安全配置,对每个到达系统的http请求链接进行校验
@Override
public void configure(HttpSecurity http) throws Exception {
//所有请求必须认证通过
http.authorizeRequests()
//下边的路径放行
.antMatchers("/v2/api-docs", "/swagger-resources/configuration/ui",
"/swagger-resources","/swagger-resources/configuration/security",
"/swagger-ui.html","/webjars/**","/course/coursepic/list/**").permitAll()
.anyRequest().authenticated();
}
注意: 通过上边的配置虽然可以访问swagger-ui,但是无法进行接口调用,除非去掉认证的配置或在上边配置中添加所有请求均放行("/**")。
此时可以访问:
Oauth2密码模式授权
Post请求:http://localhost:40400/auth/oauth/token
参数: grant_type:密码模式授权填写password
username:itcast
password:123
并且此链接需要使用 http Basic认证
该
接
口
请
求
首
先
调
用
U
s
e
r
D
e
t
a
i
l
s
S
e
r
v
i
c
e
I
m
p
l
类
中
的
l
o
a
d
U
s
e
r
B
y
U
s
e
r
n
a
m
e
方
法
。
\color{#FF0000}{该接口请求首先调用UserDetailsServiceImpl类中的loadUserByUsername方法。}
该接口请求首先调用UserDetailsServiceImpl类中的loadUserByUsername方法。
请求后结果:
将access_token字段粘贴到Authorization一栏,请求结果:
微
服
务
之
间
的
认
证
用
密
码
模
式
,
微
信
三
方
登
录
用
授
权
码
模
式
\color{#FF0000}{微服务之间的认证用密码模式,微信三方登录用授权码模式}
微服务之间的认证用密码模式,微信三方登录用授权码模式
校验令牌
Get: http://localhost:40400/auth/oauth/check_token?token=
响应结果:
该
接
口
请
求
会
重
新
调
用
U
s
e
r
D
e
t
a
i
l
s
S
e
r
v
i
c
e
I
m
p
l
类
中
的
l
o
a
d
U
s
e
r
B
y
U
s
e
r
n
a
m
e
方
法
。
\color{#FF0000}{该接口请求会重新调用UserDetailsServiceImpl类中的loadUserByUsername方法。}
该接口请求会重新调用UserDetailsServiceImpl类中的loadUserByUsername方法。
刷新令牌
刷新令牌是当令牌快过期时重新生成一个令牌,它和授权码跟密码模式生成令牌不同,刷新令牌是取授权码跟密码模式生成的令牌结果集重新生成一个令牌,只需要一个刷新令牌、客户端id和客户端密码。
Post:http://localhost:40400/auth/oauth/token
参数:
grant_type: 固定为 refresh_token
refresh_token:填写申请令牌中的refresh_token字段值
刷新令牌成功,会重新生成新的访问令牌和刷新令牌,令牌的有效期也比旧令牌长。
刷新令牌通常是在令牌快过期时进行刷新,让客户登录时间延长,不用让用户重新登录。
JWT研究
传统授权方法的问题是用户每次请求
资
源
服
务
\color{#FF0000}{资源服务}
资源服务,资源服务都需要携带令牌访问
认
证
服
务
\color{#FF0000}{认证服务}
认证服务去校验令牌的合法性,并根据令牌获取用户的相关信息,性能低下。
使用JWT的思路是,用户认证通过会得到一个JWT令牌,JWT令牌中已经包括了用户相关的信息,客户端只需要携带JWT访问资源服务,资源服务根据事先约定的算法自行完成令牌校验,无需每次都请求认证服务完成授权。
J
W
T
令
牌
就
是
为
了
解
决
不
用
每
次
请
求
认
证
服
务
完
成
授
权
。
\color{#FF0000}{JWT令牌就是为了解决不用每次请求认证服务完成授权。}
JWT令牌就是为了解决不用每次请求认证服务完成授权。
JWT令牌授权过程如下图:
什么是JWT?
JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简洁的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公钥/私钥对来签名,防止被篡改。
JWT令牌的优点:
1、JWT基于json,非常方便解析。
2、可以在令牌中自定义丰富的内容,易扩展。
3、通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。
4、资源服务使用JWT可不依赖认证服务即可完成授权。
缺点:令牌较长,存储空间比较大
令牌结构
通过学习JWT令牌结构为自定义JWT令牌打好基础。
JWT令牌由三部分组成,每部分中间使用点(.)分隔,比如:xxxxx.yyyyy.zzzzz
1、Header
包括令牌的类型(即JWT)及使用的哈希算法(如HMAC SHA256或RSA)
例如:
{
"alg": "HS256",
"typ": "JWT"
}
将上边的内容使用Base64Url编码,得到一个字符串就是JWT令牌的第一部分。
2、Payload
第二部分是负载,内容也是一个json对象,它是存放有效信息的地方,它可以存放jwt提供的现成字段,比 如:iss(签发者),exp(过期时间戳), sub(面向的用户)等,也可自定义字段。
此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。
最后将第二部分负载使用Base64Url编码,得到一个字符串就是JWT令牌的第二部分。
{
"sub": "1234567890",
"name": "456",
"admin": true
}
3、Signature
第三部分是签名,此部分用于防止jwt内容被篡改。
这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明 签名算法进行签名。
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret)
base64UrlEncode(header):jwt令牌的第一部分。
base64UrlEncode(payload):jwt令牌的第二部分。
secret:签名所使用的密钥。
JWT入门
本节我们使用Spring Security 提供的JwtHelper来创建JWT令牌,校验JWT令牌等操作。
生成私钥和公钥
JWT令牌生成采用非对称加密算法
1、生成密钥证书
下边命令生成密钥证书,采用RSA 算法每个证书包含公钥和私钥
keytool -genkeypair -alias xckey -keyalg RSA -keypass xuecheng -keystore xc.keystore -storepass xuechengkeystore
在桌面创建文件夹jwt,cmd进入该目录,输入上面指令:
查询证书信息: keytool -list -keystore xc.keystore
口令输入:xuechengkeystore
删除别名:keytool -delete -alias xckey -keystore xc.keystore
2、导出公钥
openssl是一个加解密工具包,这里使用openssl来导出公钥信息。
安装 openssl:http://slproweb.com/products/Win32OpenSSL.html
配置openssl的path环境变量,本教程配置在D:\Soft\OpenSSL-Win64\bin
cmd进入xc.keystore文件所在目录执行如下命令:
keytool ‐list ‐rfc ‐‐keystore xc.keystore | openssl x509 ‐inform pem ‐pubkey
将上边的公钥拷贝到文本文件中,合并为一行放到publickey.txt文件中。
1、生成jwt令牌
执行TestJwt类中的testCreateJwt方法生成密钥:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoieGh5In0.Y5wtZJAlirAMtQ2qQaNehDDa1uBhHWjJAHeNBXYcKP48rYpdM--oMGXZJgMa56HgXJ29YUtdKJstO8NUqJXLp7GkUvXWDBwhvmlyT82Vci7pwJitgO6YCBaYcXOSUFmWHxDX6o5RrJ0ivfU0nBE07FX30k0eBvDYnxH5FVRqo19WMBR9a36bHcYBrj9ZWsinbOmMUCgLMkNnteXHXPPH7PqTTpAmn_8C5cGQS8ER3CXXlSmRrGSn8uoYQKV87W_x7mapu6lb7vYLgVyBTOH2jUduOYg7rOD4S5JC7XJFvuyEH4z1Mew1k5NqoBBx5dhsRDJFANxoqvQpIlDbwJ1gzg
2、验证jwt令牌
执行TestJwt类中的testVerify方法进行校验
校验成功返回令牌的名字:
认证接口开发
用户登录的流程图如下:
Redis配置
1、安装Redis服务
2、安装redis-desktop-manager客户端
pom文件中添加redis
3、redis连接配置
在xc-service-ucenter-auth工程的yml文件中配置如下:
spring:
application:
name: xc-service-ucenter-auth
redis:
host: ${REDIS_HOST:127.0.0.1}
port: ${REDIS_PORT:6379}
timeout: 5000 #连接超时 毫秒
jedis:
pool:
maxActive: 3
maxIdle: 3
minIdle: 1
maxWait: -1 #连接池最大等行时间 -1没有限制
4、测试
执行RedisTest类中的testRedis方法:
返回结果,说明执行成功:
认证服务
1、登录接口
前端post提交账号、密码等,用户身份校验通过,生成令牌,并将令牌存储到redis。 将令牌写入cookie。
2、退出接口
校验当前用户的身份是否合法并且为已登录状态。 将令牌从redis删除。 删除cookie中的令牌。
流程:
1、api中的方法:
在AuthControllerApi类中添加login和logout方法;
2、yml文件中配置:
auth:
tokenValiditySeconds: 1200 #token存储到redis的过期时间
clientId: XcWebApp
clientSecret: XcWebApp
cookieDomain: xuecheng.com
cookieMaxAge: -1
3、申请令牌
为了不破坏Spring Security的代码,我们在Service方法中通过RestTemplate请求Spring Security所暴露的申请令牌接口来申请令牌。
执行TestClient类中的testClient方法测试;
4、Dao
暂时使用静态数据,待用户登录调通再连接数据库校验用户信息。
5、Service
调用认证服务申请令牌,并将令牌存储到 redis。
1、AuthToken
创建 AuthToken模型类,存储申请的令牌,包括身份令牌、刷新令牌、jwt令牌
身份令牌:用于校验用户是否认证
刷新令牌:jwt令牌快过期时执行刷新令牌
JWT令牌:用于授权。
6、Controller
在AuthController中添加、saveCookie、logout方法
登录url放行
认证服务默认都要校验用户的身份信息,这里需要将登录url放行
在WebSecurityConfig类中重写 configure(WebSecurity web)方法,如下:
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/userlogin","/userlogout","/userjwt");
}
测试认证接口:
Post请求:http://localhost:40400/auth/userlogin
测试结果:
测试写入Cookie
cookie最终会写到xuecheng.com域名下,可通过nginx代理进行认证,测试cookie是否写成功。
1、配置nginx代理
在server中添加配置代理路径:
#认证
location ^~ /openapi/auth/ {
proxy_pass http://auth_server_pool/auth/;
}
添加:
#认证服务
upstream auth_server_pool{
server 127.0.0.1:40400 weight=10;
}
POST:http://ucenter.xuecheng.com/openapi/auth/userlogin
观察cookie写入结果:
这个value就是返回值返回的短令牌