通过查看JdbcClientDetailsService可知需建立一个数据库oauth_client_details
--
-- Oauth sql -- MYSQL
--
Drop table if exists oauth_client_details;
create table oauth_client_details (
client_id VARCHAR(255) PRIMARY KEY,
resource_ids VARCHAR(255),
client_secret VARCHAR(255),
scope VARCHAR(255),
authorized_grant_types VARCHAR(255),
web_server_redirect_uri VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information TEXT,
create_time timestamp default now(),
archived tinyint(1) default '0',
trusted tinyint(1) default '0',
autoapprove VARCHAR (255) default 'false'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Drop table if exists oauth_access_token;
create table oauth_access_token (
create_time timestamp default now(),
token_id VARCHAR(255),
token BLOB,
authentication_id VARCHAR(255),
user_name VARCHAR(255),
client_id VARCHAR(255),
authentication BLOB,
refresh_token VARCHAR(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Drop table if exists oauth_refresh_token;
create table oauth_refresh_token (
create_time timestamp default now(),
token_id VARCHAR(255),
token BLOB,
authentication BLOB
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Drop table if exists oauth_code;
create table oauth_code (
create_time timestamp default now(),
code VARCHAR(255),
authentication BLOB
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Add indexes
create index token_id_index on oauth_access_token (token_id);
create index authentication_id_index on oauth_access_token (authentication_id);
create index user_name_index on oauth_access_token (user_name);
create index client_id_index on oauth_access_token (client_id);
create index refresh_token_index on oauth_access_token (refresh_token);
create index token_id_index on oauth_refresh_token (token_id);
create index code_index on oauth_code (code);
spring.jpa.database: "MYSQL"
spring.datasource.platform: "mysql"
spring.jpa.show-sql: "true"
spring.database.driverClassName: "com.mysql.jdbc.Driver"
spring.datasource.url: "jdbc:mysql://127.0.0.1:3306/eagle_eye_local?useSSL=false"
spring.datasource.username: "root"
spring.datasource.password: "root"
spring.datasource.testWhileIdle: "true"
spring.datasource.validationQuery: "SELECT 1"
spring.jpa.properties.hibernate.dialect: "org.hibernate.dialect.MySQL5Dialect"
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Arrays;
@Configuration
public class JWTOAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private TokenStore tokenStore;
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired
private TokenEnhancer jwtTokenEnhancer;
@Resource
private DataSource dataSource;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtTokenEnhancer, jwtAccessTokenConverter));
endpoints.tokenStore(tokenStore)
.accessTokenConverter(jwtAccessTokenConverter)
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// clients.withClientDetails(clientDetails());
clients.jdbc(dataSource);
// .withClient("eagleeye")
// .secret("thisissecret")
// .authorizedGrantTypes("authorization_code","refresh_token", "password", "client_credentials")
// .scopes("webclient");
// .redirectUris("http://www.baidu.com");//该地址是第三方应用注册的地址
// .accessTokenValiditySeconds(3600) // 1 hour
}
// @Bean
// public ClientDetailsService clientDetails() {
// return new JdbcClientDetailsService(dataSource);
// }
}
-- ----------------------------
-- Table structure for user_roles
-- ----------------------------
DROP TABLE IF EXISTS `user_roles`;
CREATE TABLE `user_roles` (
`user_role_id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(45) NOT NULL,
`role` varchar(45) NOT NULL,
PRIMARY KEY (`user_role_id`),
UNIQUE KEY `uni_username_role` (`role`,`username`),
KEY `fk_username_idx` (`username`),
CONSTRAINT `fk_username` FOREIGN KEY (`username`) REFERENCES `users` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user_roles
-- ----------------------------
INSERT INTO `user_roles` VALUES ('2', 'priya', 'ROLE_ADMIN');
INSERT INTO `user_roles` VALUES ('3', 'naveen', 'ROLE_USER');
INSERT INTO `user_roles` VALUES ('1', 'priya', 'ROLE_USER');
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`username` varchar(45) NOT NULL,
`password` varchar(45) NOT NULL,
`enabled` tinyint(4) NOT NULL DEFAULT '1',
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES ('naveen', 'naveen', '1');
INSERT INTO `users` VALUES ('priya', 'priya', '1');
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//方法1
auth.jdbcAuthentication().dataSource(dataSource).usersByUsernameQuery("select username,password, enabled from users where username = ?").authoritiesByUsernameQuery("select username, role from user_roles where username = ?");
//方法2
// auth.userDetailsService(userDetailsServiceBean());
//方法3
// auth.inMemoryAuthentication()
// .withUser("john.carnell").password("password1").roles("USER")
// .and()
// .withUser("william.woodward").password("password2").roles("USER", "ADMIN");
}
表名 | 字段名 | 字段说明 |
---|---|---|
oauth_client_details | client_id | 主键,必须唯一,不能为空. 用于唯一标识每一个客户端(client); 在注册时必须填写(也可由服务端自动生成). 对于不同的grant_type,该字段都是必须的. 在实际应用中的另一个名称叫appKey,与client_id是同一个概念. |
resource_ids | 客户端所能访问的资源id集合,多个资源时用逗号(,)分隔,如: "unity-resource,mobile-resource". 该字段的值必须来源于与 security.xml 中标签‹oauth2:resource-server 的属性resource-id 值一致. 在security.xml 配置有几个‹oauth2:resource-server 标签, 则该字段可以使用几个该值. 在实际应用中, 我们一般将资源进行分类,并分别配置对应的 ‹oauth2:resource-server ,如订单资源配置一个‹oauth2:resource-server , 用户资源又配置一个‹oauth2:resource-server . 当注册客户端时,根据实际需要可选择资源id,也可根据不同的注册流程,赋予对应的资源id. |
|
client_secret | 用于指定客户端(client)的访问密匙; 在注册时必须填写(也可由服务端自动生成). 对于不同的grant_type,该字段都是必须的. 在实际应用中的另一个名称叫appSecret,与client_secret是同一个概念. |
|
scope | 指定客户端申请的权限范围,可选值包括read,write,trust;若有多个权限范围用逗号(,)分隔,如: "read,write". scope的值与 security.xml 中配置的‹intercept-url 的access 属性有关系. 如‹intercept-url 的配置为 ‹intercept-url pattern="/m/**" access="ROLE_MOBILE,SCOPE_READ"/>则说明访问该URL时的客户端必须有read权限范围. write的配置值为SCOPE_WRITE, trust的配置值为SCOPE_TRUST. 在实际应该中, 该值一般由服务端指定, 常用的值为read,write. |
|
authorized_grant_types | 指定客户端支持的grant_type,可选值包括authorization_code,password,refresh_token,implicit,client_credentials, 若支持多个grant_type用逗号(,)分隔,如: "authorization_code,password". 在实际应用中,当注册时,该字段是一般由服务器端指定的,而不是由申请者去选择的,最常用的grant_type组合有: "authorization_code,refresh_token"(针对通过浏览器访问的客户端); "password,refresh_token"(针对移动设备的客户端). implicit与client_credentials在实际中很少使用. |
|
web_server_redirect_uri | 客户端的重定向URI,可为空, 当grant_type为authorization_code 或implicit 时, 在Oauth的流程中会使用并检查与注册时填写的redirect_uri是否一致. 下面分别说明:
|
|
authorities | 指定客户端所拥有的Spring Security的权限值,可选, 若有多个权限值,用逗号(,)分隔, 如: "ROLE_UNITY,ROLE_USER". 对于是否要设置该字段的值,要根据不同的grant_type来判断, 若客户端在Oauth流程中需要用户的用户名(username)与密码(password)的( authorization_code ,password ), 则该字段可以不需要设置值,因为服务端将根据用户在服务端所拥有的权限来判断是否有权限访问对应的API. 但如果客户端在Oauth流程中不需要用户信息的( implicit ,client_credentials ), 则该字段必须要设置对应的权限值, 因为服务端将根据该字段值的权限来判断是否有权限访问对应的API. |
|
access_token_validity | 设定客户端的access_token的有效时间值(单位:秒),可选, 若不设定值则使用默认的有效时间值(60 * 60 * 12, 12小时). 在服务端获取的access_token JSON数据中的 expires_in 字段的值即为当前access_token的有效时间值. 在项目中, 可具体参考 DefaultTokenServices.java 中属性accessTokenValiditySeconds . 在实际应用中, 该值一般是由服务端处理的, 不需要客户端自定义. |
|
refresh_token_validity | 设定客户端的refresh_token的有效时间值(单位:秒),可选, 若不设定值则使用默认的有效时间值(60 * 60 * 24 * 30, 30天). 若客户端的grant_type不包括 refresh_token ,则不用关心该字段 在项目中, 可具体参考DefaultTokenServices.java 中属性refreshTokenValiditySeconds . 在实际应用中, 该值一般是由服务端处理的, 不需要客户端自定义. |
|
additional_information | 这是一个预留的字段,在Oauth的流程中没有实际的使用,可选,但若设置值,必须是JSON格式的数据,如: {"country":"CN","country_code":"086"}按照 spring-security-oauth 项目中对该字段的描述 Additional information for this client, not need by the vanilla OAuth protocol but might be useful, for example,for storing descriptive information. (详见 ClientDetails.java 的getAdditionalInformation() 方法的注释) 在实际应用中, 可以用该字段来存储关于客户端的一些其他信息,如客户端的国家,地区,注册时的IP地址等等. |
|
create_time | 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段) | |
archived | 用于标识客户端是否已存档(即实现逻辑删除),默认值为'0'(即未存档). 对该字段的具体使用请参考 CustomJdbcClientDetailsService.java ,在该类中,扩展了在查询client_details的SQL加上archived = 0条件 (扩展字段) |
|
trusted | 设置客户端是否为受信任的,默认为'0'(即不受信任的,1为受信任的). 该字段只适用于grant_type="authorization_code"的情况,当用户登录成功后,若该值为0,则会跳转到让用户Approve的页面让用户同意授权, 若该字段为1,则在登录后不需要再让用户Approve同意授权(因为是受信任的). 对该字段的具体使用请参考 OauthUserApprovalHandler.java . (扩展字段) |
|
autoapprove | 设置用户是否自动Approval操作, 默认值为 'false', 可选值包括 'true','false', 'read','write'. 该字段只适用于grant_type="authorization_code"的情况,当用户登录成功后,若该值为'true'或支持的scope值,则会跳过用户Approve的页面, 直接授权. 该字段与 trusted 有类似的功能, 是 spring-security-oauth2 的 2.0 版本后添加的新属性. |
|
在项目中,主要操作 |
||
oauth_client_token | create_time | 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段) |
token_id | 从服务器端获取到的access_token 的值. |
|
token | 这是一个二进制的字段, 存储的数据是OAuth2AccessToken.java 对象序列化后的二进制数据. |
|
authentication_id | 该字段具有唯一性, 是根据当前的username(如果有),client_id与scope通过MD5加密生成的. 具体实现请参考 DefaultClientKeyGenerator.java 类. |
|
user_name | 登录时的用户名 | |
client_id | ||
该表用于在客户端系统中存储从服务端获取的token数据, 在spring-oauth-server项目中未使用到. |
||
oauth_access_token | create_time | 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段) |
token_id | 该字段的值是将access_token 的值通过MD5加密后存储的. |
|
token | 存储将OAuth2AccessToken.java 对象序列化后的二进制数据, 是真实的AccessToken的数据值. |
|
authentication_id | 该字段具有唯一性, 其值是根据当前的username(如果有),client_id与scope通过MD5加密生成的. 具体实现请参考DefaultAuthenticationKeyGenerator.java 类. |
|
user_name | 登录时的用户名, 若客户端没有用户名(如grant_type="client_credentials"),则该值等于client_id | |
client_id | ||
authentication | 存储将OAuth2Authentication.java 对象序列化后的二进制数据. |
|
refresh_token | 该字段的值是将refresh_token 的值通过MD5加密后存储的. |
|
在项目中,主要操作 |
||
oauth_refresh_token | create_time | 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段) |
token_id | 该字段的值是将refresh_token 的值通过MD5加密后存储的. |
|
token | 存储将OAuth2RefreshToken.java 对象序列化后的二进制数据. |
|
authentication | 存储将OAuth2Authentication.java 对象序列化后的二进制数据. |
|
在项目中,主要操作 |
||
oauth_code | create_time | 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段) |
code | 存储服务端系统生成的code 的值(未加密). |
|
authentication | 存储将AuthorizationRequestHolder.java 对象序列化后的二进制数据. |
|
在项目中,主要操作 |