集成开发环境
·开发工具:Eclipse/Myeclipse/IntelliJ IDEA 任选其一
·运行环境:jdk1.7及以上版本
·数据库:MySQL
spring-security-oauth2源码
·github地址:https://github.com/spring-projects/spring-security-oauth.git
构建授权服务
1、使用eclipse新建一个名称为“oauth2-server”maven项目,将其pom.xml配置文件修改为如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>oauth2-server</groupId>
<artifactId>oauth2-server</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>oauth2-server Maven Webapp</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-security</artifactId>
<version>1.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>fort-spring-security-oauth2</groupId>
<artifactId>fort-spring-security-oauth2</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>oauth2-server</finalName>
</build>
</project>
2、新建程序启动类“Application.java”,如下所示:
@SpringBootApplication
@EnableAuthorizationServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3、新建“AuthServerConfig.java”类继承“AuthorizationServerConfigurerAdapter”类初始化授权服务所需要的信息,如下所示:
@Configuration
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new JdbcTokenStore(dataSource)).authenticationManager(authenticationManager).userDetailsService(userDetailsService());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("permitAll()").allowFormAuthenticationForClients();
}
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("123").authorities("ROLE_USER", "ROLE_RESOURCE", "ROLE_API").build());
return manager;
}
}
4、新建“DataSourceConfig”类初始化数据源,如下所示:
@Configuration
public class DataSourceConfig {
@Bean(name="dataSource")
@ConfigurationProperties(prefix="spring.datasource")
public DataSource getDataSource() {
return DataSourceBuilder.create(BasicDataSource.class.getClassLoader()).build();
}
}
5、新建“application.yml”配置文件,内容如下:
spring:
datasource:
url: jdbc:mysql://host:3306/db_name?useUnicode=true&characterEncoding=UTF-8
username: root
password: 2wsx3edc
driverClassName: com.mysql.jdbc.Driver
数据库表结构
由于本示例需要将token以及client_id以及client_secret保存在数据库中,以便重启后还可再次使用,所以采用了mysql存储,有关表结构信息请参考“JdbcTokenStore.java”和“JdbcClientDetailsService.java”
本示例需要构建三个数据库表,分别为:
1、“oauth_access_token”表用来存储用户的token信息;
2、“oauth_client_details”表用来存储应用的client_id以及client_secret等信息;
3、“oauth_refresh_token”表用来存储刷新token操作的信息。
数据库表结构如下:
CREATE TABLE `oauth_access_token` (
`token_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`token` blob,
`authentication_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`user_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`client_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`authentication` blob,
`refresh_token` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `oauth_client_details` (
`client_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`client_secret` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`resource_ids` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`scope` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`authorized_grant_types` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`web_server_redirect_uri` text COLLATE utf8_unicode_ci,
`authorities` text COLLATE utf8_unicode_ci,
`access_token_validity` int(11) DEFAULT NULL,
`refresh_token_validity` int(11) DEFAULT NULL,
`additional_information` text COLLATE utf8_unicode_ci,
`autoapprove` text COLLATE utf8_unicode_ci
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `oauth_refresh_token` (
`token_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`token` blob,
`authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
启动授权服务
启动授权服务是很简单的,您只需要运行“Application.java”即可启动授权服务。
测试授权服务
授权模式
oauth2规范中具备了四种授权模式,分别如下:
·授权码模式:authorization code
·简化模式:implicit
·密码模式:resource owner password credentials
·客户端模式:client credentials
注:本示例只演示密码模式和客户端模式,感兴趣的同学自己花时间测试另外两种授权模式。
获取token
密码模式
1、通过接口“/oauth/token”获取token信息,如下所示:
$ curl -d "username=user&password=123&grant_type=password&client_id=client&client_secret=secret&scope=app" http://198.9.9.21:8080/oauth/token
返回:
{"access_token":"8f9c7a5f-b0dd-48a3-a8d0-5969f172c15e","token_type":"bearer","refresh_token":"9fba821d-cbea-40c1-bb48-e173878eb6a1","expires_in":43088,"scope":"app"}
2、通过接口“/oauth/token”刷新token,如下所示:
$ curl -d "grant_type=refresh_token&client_id=client&client_secret=secret&refresh_token=9fba821d-cbea-40c1-bb48-e173878eb6a1" http://198.9.9.21:8080/oauth/token
返回:
{"access_token":"1092a1fe-5426-47bd-8091-bdff79039f30","token_type":"bearer","refresh_token":"9fba821d-cbea-40c1-bb48-e173878eb6a1","expires_in":43199,"scope":"app"}
客户端模式
1、通过接口“/oauth/token”获取token信息,如下所示:
$ curl -d "grant_type=client_credentials&client_id=client&client_secret=secret&scope=app" http://198.9.9.21:8080/oauth/token
返回:
{"access_token":"3e40b12a-ccdf-4c93-981b-8923a2d22358","token_type":"bearer","expires_in":43199,"scope":"app"}
校验token
密码模式
1、通过接口“/oauth/check_token”校验token信息,如下所示:
$ curl -d "token=1092a1fe-5426-47bd-8091-bdff79039f30" http://client:secret@198.9.9.21:8080/oauth/check_token
返回:
{"active":true,"exp":1510678642,"user_name":"user","authorities":["ROLE_USER","ROLE_API","ROLE_RESOURCE"],"client_id":"client","scope":["app"]}
客户端模式
curl -d "token=3e40b12a-ccdf-4c93-981b-8923a2d22358" http://client:secret@198.9.9.21:8080/oauth/check_token
返回:
{"scope":["app"],"active":true,"exp":1510678854,"client_id":"client"}
注:密码模式在校验token时会返回用户权限信息,客户端模式则没有权限信息返回,所以当用户权限信息归授权服务管辖时则采用密码模式,否则采用客户端授权模式。
构建资源服务
1、使用eclipse构建一个名称为“oauth2-resource”的maven项目,将其pom.xml配置文件修改为如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>oauth2-resource</groupId>
<artifactId>oauth2-resource</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>oauth2-resource Maven Webapp</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-security</artifactId>
<version>1.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>fort-spring-security-oauth2</groupId>
<artifactId>fort-spring-security-oauth2</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Log4j -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
</dependencies>
<build>
<finalName>oauth2-resource</finalName>
</build>
</project>
2、新建程序启动类“Application.java”,如下所示:
@SpringBootApplication
@EnableResourceServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3、新建“AuthResourceConfig.java”类继承“ResourceServerConfigurerAdapter”类初始化资源服务所需要的信息,如下所示:
@Configuration
public class AuthResourceConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setClientId("client");
tokenService.setClientSecret("secret");
tokenService.setCheckTokenEndpointUrl("http://198.9.9.21:8080/oauth/check_token");
resources.tokenServices(tokenService);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/private/data").hasRole("RESOURCE")
.antMatchers("/api/data").permitAll();
}
}
4、新建“ResourceController.java”类构建资源服务所提供的接口,分别为 “/private/data”和“/api/data”,如下所示:
@RestController
public class ResourceController {
@RequestMapping("/private/data")
public String getResource() {
Object details = SecurityContextHolder.getContext().getAuthentication().getDetails();
OAuth2AuthenticationDetails oAuth2Authen = (OAuth2AuthenticationDetails) details;
System.out.println(oAuth2Authen.getTokenType());
System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
return "Hello! This is private data.";
}
@RequestMapping("/api/data")
public String getApiData() {
Object details = SecurityContextHolder.getContext().getAuthentication().getDetails();
if(details instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails oAuth2Authen = (OAuth2AuthenticationDetails) details;
System.out.println(oAuth2Authen.getTokenType());
System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
return "Hello! This is api data.";
}
return "error";
}
}
5、新建“application.yml”配置文件,内容如下:
server:
port: 9001
启动资源服务
我们启动授权服务是通过运行“Application.java”来启动,同理也可以运行资源服务中的“Application.java”来启动资源服务。
资源服务接口权限测试
未授权访问接口
1、访问“/private/data”,如下所示:
$ curl http://198.9.9.21:9001/private/data
返回
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
2、访问“/api /data”,如下所示:
$ curl http://198.9.9.21:9001/api/data
返回
error
使用token访问接口
1、访问“/private/data”,如下所示:
·使用密码模式授权token访问
$ curl -d "usernaH "Authorization:Bearer 01a65ff5-cec7-4e2e-8812-69abac4ad566" http://198.9.9.21:9001/private/data
返回
Hello! This is private data.
·使用客户端模式授权token访问
$ curl -H "Authorization:Bearer 3e40b12a-ccdf-4c93-981b-8923a2d22358" http://198.9.9.21:9001/private/data
返回
{"error":"access_denied","error_description":"Access is denied"}
2、访问“/api /data”,如下所示:
$ curl -H "Authorization:Bearer 3e40b12a-ccdf-4c93-981b-8923a2d22358" http://198.9.9.21:9001/api/data
返回
Hello! This is api data.