概念
OAuth的实现思路:OAuth在"客户端"与"服务提供商"之间,设置了一个授权层(authorization layer)。“客户端"不能直接登录"服务提供商”,只能登录授权层,以此将用户与客户端区分开来。"客户端"登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。 "客户端"登录授权层以后,"服务提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。
为了使资源访问安全,设置给用户携带token访问资源。达到的效果如下面流程图:
- 流程图
2.使用账号密码到授权服务获取token
3.携带token
重新请求资源,返回资源信息
实践
授权服务
创建授权服务,用于生成token
、验证token
,首先创建Springboot
项目,然后进行如下步骤:
- 增加
maven
依赖 - 配置数据库地址、增加
token
的数据库存储配置 - 初始化数据库
步骤1:添加maven依赖
引入oauth2
与mysql
依赖,mysql
用来存储和配置token
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- 实现对 Spring MVC 的自动配置 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> <version>2.2.5.RELEASE</version></dependency><!-- 实现对数据库连接池的自动化配置 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope></dependency><dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.18</version></dependency>
步骤2:添加主要配置
- application.yml
application.yml
增加数据库配置信息
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.cj.jdbc.Driverdruid:url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&useSSL=falseusername: adminpassword: qwe234
- OAuth2AuthorizationServerConfig
设置token存储与配置,设置使用 JdbcTokenStore
和 ClientDetailsService
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate DataSource dataSource;@Beanpublic TokenStore jdbcTokenStore(){return new JdbcTokenStore(dataSource);}// token存储到数据库中@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.authenticationManager(authenticationManager).tokenStore(jdbcTokenStore());}@Overridepublic void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {oauthServer.checkTokenAccess("isAuthenticated()");}@Beanpublic ClientDetailsService jdbcClientDetailsService() {return new JdbcClientDetailsService(dataSource);}@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.withClientDetails(jdbcClientDetailsService());}
}
- SecurityConfig
配置使用jdbcAuthentication
认证
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate DataSource dataSource;@Override@Bean(name = BeanIds.AUTHENTICATION_MANAGER)public AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Beanpublic static NoOpPasswordEncoder passwordEncoder() {return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.jdbcAuthentication().dataSource(dataSource);}
}
步骤3:初始化数据库
-- ----------------------------
-- Table structure for oauth_access_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token`(`token_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`token` mediumblob NULL,`authentication_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,`user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`client_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`authentication` mediumblob NULL,`refresh_token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,PRIMARY KEY (`authentication_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for oauth_approvals
-- ----------------------------
DROP TABLE IF EXISTS `oauth_approvals`;
CREATE TABLE `oauth_approvals`(`userId` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`clientId` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`scope` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`expiresAt` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP,`lastModifiedAt` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`(`client_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,`resource_ids` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`client_secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`scope` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`authorized_grant_types` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`web_server_redirect_uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`authorities` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`access_token_validity` int(0) NULL DEFAULT NULL,`refresh_token_validity` int(0) NULL DEFAULT NULL,`additional_information` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`autoapprove` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
INSERT INTO `oauth_client_details` VALUES ('tokendemo', NULL, '1qa233', 'read_userinfo,read_contacts', 'password,refresh_token,authorization_code', 'http://127.0.0.1:8080/login', NULL, 3600, 864000, NULL, '1');
-- ----------------------------
-- Table structure for oauth_client_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_token`;
CREATE TABLE `oauth_client_token`(`token_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`token` mediumblob NULL,`authentication_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,`user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`client_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,PRIMARY KEY (`authentication_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for oauth_code
-- ----------------------------
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code`(`code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`authentication` mediumblob NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token`(`token_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`token` mediumblob NULL,`authentication` mediumblob NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
资源服务
资源服务改造,主要包括如下几步:
- 添加maven依赖
- 增加OAuth2.0配置信息
- 设置授权服务地址
- 登录接口demo
步骤1:添加maven依赖
添加maven依赖
<dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> <version>2.1.2.RELEASE</version>
</dependency>
步骤2:增加OAuth2.0配置信息
@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {@Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests()// 设置 /login 无需权限访问.antMatchers("/login").permitAll()// 设置其它请求,需要认证后访问.anyRequest().authenticated();}
}
步骤3:设置授权服务地址
security:oauth2:# OAuth2 Client 配置,对应 OAuth2ClientProperties 类client:client-id: tokendemoclient-secret: 1qa233user-authorization-uri: http://127.0.0.1:8082/oauth/authorize # 获取用户的授权码地址access-token-uri: http://127.0.0.1:8082/oauth/token # 获取访问令牌的地址# OAuth2 Resource 配置,对应 ResourceServerProperties 类resource:client-id: tokendemouser-info-uri: http://127.0.0.1:8082/authorize # 获取用户的授权码地址token-info-uri: http://127.0.0.1:8082/oauth/check_token# 获取用户的授权码验证地址
步骤4:登录接口demo
调用登录接口时,使用用户名/密码去授权服务获取token
,后面可以用这个token
访问资源服务的其他接口
@RestController
@RequestMapping("/login")
public class LoginController {@Value("${security.oauth2.client.access-token-uri}")private String accessTokenUri;@Value("${security.oauth2.client.client-id}")private String clientId;@Value("${security.oauth2.client.client-secret}")private String clientSecret;@PostMappingpublic OAuth2AccessToken login(@RequestParam("username") String username, @RequestParam("password") String password) {ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();resourceDetails.setAccessTokenUri(accessTokenUri);resourceDetails.setClientId(clientId);resourceDetails.setClientSecret(clientSecret);resourceDetails.setUsername(username);resourceDetails.setPassword(password);// 请求授权服务,获取tokenOAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);restTemplate.setAccessTokenProvider(new ResourceOwnerPasswordAccessTokenProvider());return restTemplate.getAccessToken();}
}
验证
- login获取token
请求login
接口可以看到返回值如下:
![](https://i-blog.csdnimg.cn/blog_migrate/58eab4e7ab2a38ef0dc51bb4a748f5ab.png)
- 访问资源
携带token访问资源如下:
![](https://i-blog.csdnimg.cn/blog_migrate/e3534b5d53ffe6f0773216406ea90fad.png)
总结
以上就是快速集成oauth,简单的使用了mysql作为存储,正常开发推荐使用redis存储,redis的过期机制与token过期更加契合。