1.基于OAuth 2.0+JWT+spring security完成认证授权
首先分析一下为什么要用OAuth2和JWT来做
- 单点登录(SSO) 方案单击登录方案是最常见的解决方案,但单点登录需要每个与用户交互的服务都必须与认证服务进行通信,这不但会造成重复,也会产生大量琐碎的网络流量;
- 分布式会话(Session) 方案通过将用户会话信息存储在共享存储中,如Redis,并使用用户会话的ID作为key来实现分布式哈希映射。当用户访问微服务时,会话数据就可以从共享存储中获取。该解决方案在高可用和扩展方面都很好,但是由于会话信息保存在共享存储中,所以需要一定的保护机制保护数据安全,因此在具体的实现中会具有比较高的复杂度。
- 客户端令牌(Token) 方案令牌由客户端生成,并由认证服务器签名。在令牌中会包含足够的信息,客户端在请求时会将令牌附加在请求上,从而为各个微服务提供用户身份数据。此方案解决了分布式会话方案的安全性问题,但如何及时注销用户认证信息则是一个大问题,虽然可以使用短期令牌并频繁地与认证服务器进行校验,但并不可以彻底解决。JWT(JSON Web Tokens)是非常出名的客户端令牌解决方案,它足够简单,并且对各种环境支持程度也比较高
- 客户端令牌与API网关结合
通过在微服务架构中实施API网关,可以将原始的客户端令牌转换为内部会话令牌。一方面可以有效地隐藏微服务,另一方面通过API网关的统一入口可以实现令牌的注销处理。在David Borsos的第二个方案:分布式Session方案中要求开发者能够将用户会话信息单独拎出来进行集中管理。业界比较成熟的开源项目有Spring Session,其使用Redis数据库或缓存机制来实现Session存储,并通过过滤器实现Session数据的自动加载。随着近几年云服务应用的发展,基于令牌(Token)的认证使用范围也越来越广。对于基于令牌认证通常包含下面几层含义:
- 令牌是认证用户信息的集合,而不仅仅是一个无意义的ID。
- 在令牌中已经包含足够多的信息,验证令牌就可以完成用户身份的校验,从而减轻了因为用户验证需要检索数据库的压力,提升了系统性能。
- 因为令牌是需要服务器进行签名发放的,所以如果令牌通过解码认证,我们就可以认为该令牌所包含的信息是合法有效的。
- 服务器会通过HTTP头部中的Authorization获取令牌信息并进行检查,并不需要在服务器端存储任何信息。
- 通过服务器对令牌的检查机制,可以将基于令牌的认证使用在基于浏览器的客户端和移动设备的App或是第三方应用上。
·可以支持跨程序调用。基于Cookie是不允许垮域访问的,而令牌则不存在这个问题。
综上所述,基于令牌的认证由于会包含认证用户的相关信息,因此可以通过验证令牌来完成用户身份的校验,完全不同于之前基于会话的认证。因此,基于令牌的这个优点,像T微信、支付宝、微博及GitHub等,都推出了基于令牌的认证服务,用于访问所开放的API及单点登录。接下来将重点介绍基于令牌认证方案中的OAuth 2.0和JWT
2.两部分:认证服务端(认证及生成token) 、认证资源服务端(访问其他服务内的资源需要校验)
3.先看看 客户端授权模式(一般采用授权码模式,简单来说就是你要重定向url认证服务器获取授权码(code),在获取访问令牌。
在上面流程图中第一步之后,会重定向到类似于
http://localhost:8080/token/oauth/authorize?client_id=client1&response_type=code&redirect_uri=/token
会返回一个code
在访问
http://localhost:8080/oauth/token?client_id=client1&grant_type=authorization_code&redirect_uri=/token&code=
附加上code值,这时就会返回access_token
还有一个问题:上面url的请求的路径需要保存在数据库中,需要新建一个表,固定的字段
CREATE TABLE `oauth_client_details` (
`client_id` varchar(255) NOT NULL,
`resource_ids` varchar(255) DEFAULT NULL,
`client_secret` varchar(255) DEFAULT NULL,
`scope` varchar(255) DEFAULT NULL,
`authorized_grant_types` varchar(255) DEFAULT NULL,
`web_server_redirect_uri` varchar(255) DEFAULT NULL,
`authorities` varchar(255) DEFAULT NULL,
`access_token_validity` int(11) DEFAULT NULL,
`refresh_token_validity` int(11) DEFAULT NULL,
`additional_information` varchar(4096) DEFAULT NULL,
`autoapprove` varchar(255) DEFAULT NULL,
PRIMARY KEY (`client_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
数据库保存授权用户
CREATE TABLE `sys_user` (
`id` varchar(150) NOT NULL,
`phone` varchar(50) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`password` varchar(150) NOT NULL,
`disable` int(11) NOT NULL,
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`ip` varchar(150) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
详情见如后代码
4.正式代码 (认证服务端)
pom.xml文件
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
</dependency>
主要讲解这三个类,他们的的执行顺序:SecurityConfiguration->MyAuthenticationSuccessHandler->AuthorizationServerConfiguration
首先在用户中心登陆操作,获取账号密码,发送HTTP请求
private JSONObject requestToken(String account, String password, String deviceType) {
String result = null;
try {
RestTemplate restTemplate = new RestTemplate();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
HttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()