| 知乎专栏 |
<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> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
http://localhost:8080/oauth/authorize?response_type=code&client_id=sso&redirect_uri=http://localhost:8082/callback&scope=read http://localhost:8080/oauth/token?client_id=sso&grant_type=authorization_code&redirect_uri=http://localhost:8082/callback&code=ZzLi3w localhost:8082/admin&code=ZzLi3w localhost:8080/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.netkiller.cn&state=123 localhost:8080/oauth/authorize?response_type=code&client_id=sso&redirect_uri=http://localhost:8082/test&scope=app http://localhost:8080/oauth/token?client_id=sso&grant_type=authorization_code&redirect_uri=http://localhost:8082&code=8z5J9L http://localhost:8080/oauth/authorize?response_type=code&client_id=sso&redirect_uri=http://localhost:8080&scope=app http://www.netkiller.cn/?code=eX6IMV&state=123 http://localhost:18084/oauth/token?client_id=client&grant_type=authorization_code&redirect_uri=http://api.netkiller.cn&code=bzxoHn
下面例子由三个项目组成,分别是 tools, server, client。
其中 tools 是密码生成工具
https://tools.ietf.org/html/rfc6749
<?xml version="1.0" encoding="UTF-8"?> <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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.netkiller</groupId> <artifactId>oauth</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>oauth</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </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>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> </dependencies> <modules> <module>server</module> <module>client</module> </modules> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Maven
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.netkiller</groupId>
<artifactId>oauth</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>tools</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>tools</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
下面的代码用于生成 Spring security 所用的密码
package cn.netkiller.oauth.tools;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* Hello world!
*
*/
public class App {
public static void main(String[] args) {
int i = 0;
while (i < 10) {
String password = "123456";
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String hashedPassword = passwordEncoder.encode(password);
System.out.println(hashedPassword);
i++;
}
}
}
运行后生成类似的密码
$2a$10$IrDG5Yr3CGorEg9gKG8smeRLnNUieCVyBCJHA80U6h20HDFCxd43W $2a$10$wseh5xFv1L3a3uHQId0MAOqAN0rKKcuX.RDaBPQ.pV/hsr80TqwxO $2a$10$xP3Gc/5/PN03BdkDfhUjAemTRVaiwr0lsaqPqD18UI.ho9nRC/ebW $2a$10$S.wLZ6e5YvmQA6mkX8yXWOdJbvahtDOesRu0ZwPOzAPCwpo7eDAsi $2a$10$Jo/yuWyiAZ2Lj8.ywoPl7OeOJYuP7RVq8l.qc/zOwtW8MTFp3NYGO $2a$10$eEvvjPok0fRK.DU6yF0qI.aucuiWr3y5G93SLq9/76ovcOwIuQAuS $2a$10$BWEkANxbgwATNQCEI9/uNevNEUNlomGY7cZ2CQVm.qCRcnyukT.Si $2a$10$69wSpyJQvjzJY7ou5PFWlOlEIecQukHV9WEq0nebsz5V6IZKfOVv2 $2a$10$Cyj3hM39V34r5pMeQ.Y9peuUqYMBSvsJ7GTBgp4.stWaTtWMboYGS $2a$10$0/o4cRN2.tmnc58sH.N4WOsreVI6sWlPl4CCBrmUfJ332TMfRzA42
<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>cn.netkiller</groupId> <artifactId>oauth</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>cn.netkiller</groupId> <artifactId>server</artifactId> <name>server</name> <description>Example Spring Boot REST project</description> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> </dependencies> </project>
server.port=8000 server.contextPath=/api logging.level.com.gigy=DEBUG # Data source properties spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://127.0.0.1:3306/netkiller?useSSL=false spring.datasource.username=netkiller spring.datasource.password=123123 spring.datasource.max-idle=10 spring.datasource.max-wait=10000 spring.datasource.min-idle=5 spring.datasource.initial-size=5 spring.datasource.validation-query=SELECT 1 spring.datasource.test-on-borrow=false spring.datasource.test-while-idle=true spring.datasource.time-between-eviction-runs-millis=18800 spring.datasource.jdbc-interceptors=ConnectionState;SlowQueryReport(threshold=0) spring.jpa.database=MYSQL spring.jpa.show-sql=true #spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.ddl-auto=create-drop #spring.jpa.hibernate.ddl-auto=validate #spring.jpa.show-sql=true
package cn.netkiller.oauth.server.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
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.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {
@Autowired
@Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
@Autowired
private AuthenticationManager authenticationManager;
@Value("${oauth.tokenTimeout:3600}")
private int expiration;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
configurer.authenticationManager(authenticationManager);
configurer.userDetailsService(userDetailsService);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("api").secret("secret").accessTokenValiditySeconds(expiration).scopes("read", "write").authorizedGrantTypes("password", "refresh_token").resourceIds("resource");
}
}
Spring boot 2.0
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("api").secret(passwordEncoder().encode("secret")).accessTokenValiditySeconds(expiration).scopes("read", "write").authorizedGrantTypes("password", "refresh_token").resourceIds("resource");
}
package cn.netkiller.oauth.server.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
@Configuration
@EnableWebSecurity
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
/**
* Constructor disables the default security settings
*/
public WebSecurityConfiguration() {
super(true);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/login");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**").authorizeRequests().anyRequest().authenticated();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
package cn.netkiller.oauth.server.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@Entity
@Table(name = "users")
public class User implements UserDetails {
static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "user_id", nullable = false, updatable = false)
private Long id;
@Column(name = "username", nullable = false, unique = true)
private String username;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "enabled", nullable = false)
private boolean enabled;
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
return authorities;
}
public boolean isAccountNonExpired() {
return true;
}
public boolean isAccountNonLocked() {
// we never lock accounts
return true;
}
public boolean isCredentialsNonExpired() {
// credentials never expire
return true;
}
public boolean isEnabled() {
return enabled;
}
public String getPassword() {
return password;
}
public String getUsername() {
return username;
}
}
package cn.netkiller.oauth.server.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import cn.netkiller.oauth.server.model.User;
public interface UserRepository extends JpaRepository<User, Long> {
/**
* Find a user by username
*
* @param username
* the user's username
* @return user which contains the user with the given username or null.
*/
User findOneByUsername(String username);
}
package cn.netkiller.oauth.server.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import cn.netkiller.oauth.server.repository.UserRepository;
@Service("userDetailsService")
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findOneByUsername(username);
}
}
package cn.netkiller.oauth.server.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class TestRestController {
@RequestMapping(value = "hello", method = RequestMethod.GET)
public ResponseEntity<String> hello() {
return new ResponseEntity<String>("Hello world !", HttpStatus.OK);
}
@PreAuthorize("#oauth2.hasScope('write')")
@RequestMapping(value = "set/{string}", method = RequestMethod.GET)
public ResponseEntity<String> set(String string) {
return new ResponseEntity<String>(string, HttpStatus.OK);
}
}
在 src/main/resources 目录创建 data.sql 文件
INSERT INTO users (user_id, username, password, enabled) VALUES
('1', 'netkiller@msn.com', '$2a$10$Cyj3hM39V34r5pMeQ.Y9peuUqYMBSvsJ7GTBgp4.stWaTtWMboYGS', true);
此处密码 $2a$10$Cyj3hM39V34r5pMeQ.Y9peuUqYMBSvsJ7GTBgp4.stWaTtWMboYGS 请使用上面提供的工具生成。
启动 Spring boot Server 项目
mvn spring-boot:run
启动后 Spring boot 会导入 data.sql 文件
mysql> select * from users where username="netkiller@msn.com"; +---------+---------+--------------------------------------------------------------+-------------------+ | user_id | enabled | password | username | +---------+---------+--------------------------------------------------------------+-------------------+ | 4 | | $2a$10$Cyj3hM39V34r5pMeQ.Y9peuUqYMBSvsJ7GTBgp4.stWaTtWMboYGS | netkiller@msn.com | +---------+---------+--------------------------------------------------------------+-------------------+ 1 row in set (0.00 sec)
MacBook-Pro:Application neo$ curl -X POST --user 'api:secret' -d 'grant_type=password&username=netkiller@msn.com&password=123456' http://localhost:8000/api/oauth/token
{"access_token":"5bc0ee89-cd6d-47be-b31f-89c9e028159b","token_type":"bearer","refresh_token":"5107c09b-de85-4faf-8396-941572cf30d2","expires_in":3599,"scope":"read write"}MacBook-Pro:Application neo$
MacBook-Pro:Application neo$ curl -H "Accept: application/json" -H "Content-Type: application/json" -H "Authorization: Bearer 5bc0ee89-cd6d-47be-b31f-89c9e028159b" -X GET http://localhost:8000/api/test/hello
Hello world !
<dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency>
package cn.netkiller.config;
import static java.util.Arrays.asList;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
@EnableOAuth2Client
@Configuration
public class OAuth2ClientConfiguration {
@Value("${oauth.resource:http://localhost:8080}")
private String baseUrl;
@Value("${oauth.authorize:http://localhost:8080/oauth/authorize}")
private String authorizeUrl;
@Value("${oauth.token:http://localhost:8080/oauth/token}")
private String tokenUrl;
@Bean
protected OAuth2ProtectedResourceDetails resource() {
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setAccessTokenUri(tokenUrl);
resource.setClientId("api");
resource.setClientSecret("secret");
resource.setGrantType("password");
resource.setScope(asList("read", "write"));
resource.setUsername("netkiller@msn.com");
resource.setPassword("123456");
return resource;
}
@Bean
public OAuth2RestOperations restTemplate() {
AccessTokenRequest accessTokenRequest = new DefaultAccessTokenRequest();
return new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(accessTokenRequest));
}
}
package cn.netkiller;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
System.out.println("Spring boot with Oauth2RestTemplate!");
SpringApplication.run(Application.class, args);
}
}
package cn.netkiller.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class TestController {
@Autowired
private OAuth2RestOperations restTemplate;
@GetMapping("/")
@ResponseBody
public String index() {
OAuth2AccessToken token = restTemplate.getAccessToken();
System.out.println(token.getValue());
String tmp = restTemplate.getForObject("http://api.netkiller.cn/test.json", String.class);
System.out.println(tmp);
return tmp;
}
}
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency>
如果是 Springboot 2.0.4 需要制定版本号
<dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>2.3.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> <version>1.0.9.RELEASE</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-core</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.sun.activation</groupId> <artifactId>javax.activation</artifactId> <version>1.2.0</version> </dependency>
package api.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
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.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {
@Autowired
@Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
@Autowired
private AuthenticationManager authenticationManager;
@Value("${oauth.tokenTimeout:3600}")
private int expiration;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// @Override
// public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
// oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')");
// oauthServer.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
// }
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("api").secret(passwordEncoder().encode("secret")).accessTokenValiditySeconds(expiration).refreshTokenValiditySeconds(expiration).scopes("read", "write").authorizedGrantTypes("password", "refresh_token").authorities("USER").resourceIds("blockchain");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
configurer.tokenStore(tokenStore()).accessTokenConverter(accessTokenConverter());
configurer.authenticationManager(authenticationManager);
configurer.userDetailsService(userDetailsService);
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
package api.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
@Configuration
@EnableResourceServer
public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
public ResourceServerConfigurer() {
// TODO Auto-generated constructor stub
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId("blockchain");
resources.tokenServices(tokenServices());
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
}
}
package api.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/login");
}
@Override
public void configure(HttpSecurity http) throws Exception {
// http.antMatcher("/**").authorizeRequests().anyRequest().authenticated();
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll().anyRequest().authenticated().and().httpBasic().and().csrf().disable();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
INSERT INTO `user` (username, password, enabled) VALUES ('netkiller@msn.com', '$2a$10$Cyj3hM39V34r5pMeQ.Y9peuUqYMBSvsJ7GTBgp4.stWaTtWMboYGS', true);
neo@MacBook-Pro ~ % curl -X POST --user 'api:secret' -d 'grant_type=password&username=netkiller@msn.com&password=123456' http://localhost:8080/oauth/token
{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzb3VyY2UiXSwiZXhwIjoxNTM1MTE4MzYxLCJ1c2VyX25hbWUiOiJuZXRraWxsZXJAbXNuLmNvbSIsImp0aSI6ImM5YzA4ODczLWZlMTctNGM2My05MDA1LTIzZGJlNTE1NTQ0YiIsImNsaWVudF9pZCI6ImFwaSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdfQ.ydxJQKtdBNWHELL8_aXVoZETNMygXs8T1HQ5XWDBELA","token_type":"bearer","refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzb3VyY2UiXSwidXNlcl9uYW1lIjoibmV0a2lsbGVyQG1zbi5jb20iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiYzljMDg4NzMtZmUxNy00YzYzLTkwMDUtMjNkYmU1MTU1NDRiIiwiZXhwIjoxNTM1MTE4MzYxLCJqdGkiOiI3ODJiNmY2NC0xYzdiLTQ0YWYtOThlZC1iZDk3Y2QzYzE1NjEiLCJjbGllbnRfaWQiOiJhcGkifQ.16QUHOrVPUBF97E_972AcLA83zEK7UzaI424PeAmX6E","expires_in":3599,"scope":"read write","jti":"c9c08873-fe17-4c63-9005-23dbe515544b"}
复制 access_token 粘贴到 Authorization: Bearer 后面
neo@MacBook-Pro ~ % curl -H "Accept: application/json" -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYmxvY2tjaGFpbiJdLCJleHAiOjE1MzUxMTkyMTUsInVzZXJfbmFtZSI6Im5ldGtpbGxlckBtc24uY29tIiwianRpIjoiZTNjYmIxNTItYmFkZS00MGM1LTkxOGItZjJjMzZiYTBiMTE2IiwiY2xpZW50X2lkIjoiYXBpIiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl19.ophWAvIT1ZmWCIQyAgyuzmQ98TJsN49OeR3CBSJ_X54" -X GET http://localhost:8080/test/mongo
{"id":"5b800709e18a211e83c7f355","name":"Netkiller"}
URL=http://localhost:8080
TOKEN=$(curl -s -X POST --user 'api:secret' -d 'grant_type=password&username=netkiller@msn.com&password=123456' ${URL}/oauth/token | sed 's/.*"access_token":"\([^"]*\)".*/\1/g')
curl -s -k -H "Accept: application/json" -H "Content-Type: application/json" -H "Authorization: Bearer ${TOKEN}" -X GET ${URL}/test/hello.json
curl -s -X POST --user 'api:secret' -d 'grant_type=refresh_token&client_id=api&client_secret=secret&refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYmxvY2tjaGFpbiJdLCJ1c2VyX25hbWUiOiJibG9ja2NoYWluIiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImF0aSI6ImU2OGE4ODUzLWJlODUtNGUwNy1hYzE0LTM0MDI1ZDBiZGY0ZiIsImV4cCI6MTU0MDM4MzA5NCwianRpIjoiY2JjNTk2ZWMtOTkyZi00NmQ2LWFiZGYtMTcyYWFiZmEwNDFiIiwiY2xpZW50X2lkIjoiYXBpIn0.PqsGl5Dm8WBqwbhHf-LqmUP1toCpsFS4gLeOP2bMwR4' ${URL}/oauth/token
创建证书 keytool -genkeypair -alias jwt -keyalg RSA -keypass passw0rd -keystore jwt.jks -storepass passw0rd
neo@MacBook-Pro /tmp/oauth % keytool -genkeypair -alias jwt -keyalg RSA -keypass passw0rd -keystore jwt.jks -storepass passw0rd What is your first and last name? [Unknown]: Neo Chen What is the name of your organizational unit? [Unknown]: netkiller.cn What is the name of your organization? [Unknown]: netkiller.cn What is the name of your City or Locality? [Unknown]: Shenzhen What is the name of your State or Province? [Unknown]: Guangdong What is the two-letter country code for this unit? [Unknown]: CN Is CN=Neo Chen, OU=netkiller.cn, O=netkiller.cn, L=Shenzhen, ST=Guangdong, C=CN correct? [no]: yes
该命令将生成一个名为jwt.jks的文件,其中包含我们的密钥 - 公钥和私钥。 还要牢记keypass和storepass密码。
导出公钥,接下来,我们需要从刚刚生成的JKS中导出我们的公钥,我们可以使用下面的命令来实现:
neo@MacBook-Pro /tmp/oauth % keytool -list -rfc --keystore jwt.jks | openssl x509 -inform pem -pubkey -out certificate.crt > public.crt Enter keystore password: passw0rd
公钥内容
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj6ePdDwBrHKX3kNFnbve T1rTTbyA9GjaiZNwj2X4Y0In7RCFl8auXXBn2DxztQMGqHY2Ydc3/26Gu9Vri441 r8/RInA6UpzzDRl5SeYYTobcgfIVpfQ0hTX0xzuMDVLVoLibGfcvGy7ZkrJjQFX8 lIaO84K8KP/yzma5622XJ+f5hkXmTX5e0tXGDCPjVO1dSrouPWqhcbM0Kf6y3RdE JkNRTHLky6afx8MNobakz1Ab9K7cjD8De6LwScwMQMFU46traN/3Fw0lZFxKkpay +sEUHvHDUYWTuVovUmfiKMX8fj5QCm4imPdA3pF/jjM+xeeVcTID3qffDGOKrGTF HQIDAQAB -----END PUBLIC KEY-----
复制 jwt.jks 和 public.crt 到 src/main/resources 目录下
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "passw0rd".toCharArray());
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("passw0rd"));
return converter;
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
String publicKey = "-----BEGIN PUBLIC KEY-----\n" +
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj6ePdDwBrHKX3kNFnbve\n" +
"T1rTTbyA9GjaiZNwj2X4Y0In7RCFl8auXXBn2DxztQMGqHY2Ydc3/26Gu9Vri441\n" +
"r8/RInA6UpzzDRl5SeYYTobcgfIVpfQ0hTX0xzuMDVLVoLibGfcvGy7ZkrJjQFX8\n" +
"lIaO84K8KP/yzma5622XJ+f5hkXmTX5e0tXGDCPjVO1dSrouPWqhcbM0Kf6y3RdE\n" +
"JkNRTHLky6afx8MNobakz1Ab9K7cjD8De6LwScwMQMFU46traN/3Fw0lZFxKkpay\n" +
"+sEUHvHDUYWTuVovUmfiKMX8fj5QCm4imPdA3pF/jjM+xeeVcTID3qffDGOKrGTF\n" +
"HQIDAQAB\n" +
"-----END PUBLIC KEY-----";
converter.setVerifierKey(publicKey);
return converter;
}
//
// AppDelegate.m
//
// Created by Apple on 2018/9/8.
// Copyright © 2018年 Apple. All rights reserved.
//
#import "AppDelegate.h"
#import "YTKNetworkConfig.h"
#import "AFOAuth2Manager.h"
#import <AFNetworking.h>
#import "NSAppConfig.h"
@interface AppDelegate ()
@property (nonatomic, strong) AFOAuthCredential *credential;
@property (nonatomic, strong) AFOAuth2Manager *OAuth2Manager;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//授权
NSURL *url = [NSURL URLWithString:BASE_URL];
AFOAuth2Manager *OAuth2Manager = [[AFOAuth2Manager alloc]initWithBaseURL:url clientID:CLIENTID secret:SECRET];
self.OAuth2Manager = OAuth2Manager;
[OAuth2Manager authenticateUsingOAuthWithURLString:OAUTH_TOKEN
username:OAUTH_USERNAME
password:OAUTH_PASSWORD
scope:@""
success:^(AFOAuthCredential *credential) {
// NSLog(@"********请求OauthToek成功***********");
self.credential = credential;
[self testPost];
[self testGet];
}
failure:^(NSError *error) {
// NSLog(@"********请求OauthToekn失败begin***********\r\n%@\r\n*********请求OauthToekn失败end************",error);
}];
// YTKNetworkConfig *config = [YTKNetworkConfig sharedConfig]; config.baseUrl = @"http://yuantiku.com";
//
// Override point for customization after application launch.
return YES;
}
- (void)testGet{
NSURL *baseURL = [NSURL URLWithString:@"http://192.168.0.185:8080"];
AFHTTPSessionManager *manager =
[[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];
[manager.requestSerializer setAuthorizationHeaderFieldWithCredential:self.credential];
[manager GET:@"/test/hello"
parameters:nil
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"Success: %@", responseObject);
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"Failure: %@", error);
}];
}
- (void)testPost{
NSURL *baseURL = [NSURL URLWithString:@"http://192.168.0.185:8080"];
AFHTTPSessionManager *manager =
[[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setAuthorizationHeaderFieldWithCredential:self.credential];
NSDictionary *dic = @{@"mobile":@"13113676543",
@"password":@"123456",
@"role":@"Organization"
};
[manager POST:@"/member/create"
parameters:dic
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"Success: %@", responseObject);
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"Failure: %@ %@", error,task);
}];
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
@end
Post 出去的数据是 Raw 格式。
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
//申明返回的结果是json类型
manager.responseSerializer = [AFJSONResponseSerializer serializer];
//申明请求的数据是json类型
manager.requestSerializer=[AFJSONRequestSerializer serializer];
//如果报接受类型不一致请替换一致text/html或别的
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html"];
//传入的参数
NSDictionary *parameters = @{@"1":@"XXXX",@"2":@"XXXX",@"3":@"XXXXX"};
//你的接口地址
NSString *url=@"http://xxxxx";
//发送请求
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@", error);
}];
环境:Java11 + Springboot 2.1.3
<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>cn.netkiller</groupId> <artifactId>parent</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>cn.netkiller</groupId> <artifactId>oauth2-client</artifactId> <version>0.0.1-SNAPSHOT</version> <name>oauth2-client</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
server:
port: 8080
APP-CLIENT-ID: 180579ba67875ffc18e3
APP-CLIENT-SECRET: 8175af8f5691e2f3b6007b9597d8c1e3499e15a0
spring:
# redis:
# host: localhost
security:
oauth2:
client:
provider:
netkiller:
authorization-uri: http://localhost:8080/oauth/authorize
token-uri: http://localhost:8080/oauth/token
user-info-uri: http://localhost:8080/user
user-name-attribute: name
# token-endpoint-url: http://localhost:8080/oauth/check_token
yahoo-oidc:
issuer-uri: https://api.login.yahoo.com
registration:
netkiller:
client-id: sso
client-secret: secret
client-name: Neo
provider: netkiller
scope: read
authorization-grant-type: authorization_code
redirect-uri: http://localhost:${server.port}/login/oauth2/code/netkiller
github-client-1:
client-id: ${APP-CLIENT-ID}
client-secret: ${APP-CLIENT-SECRET}
client-name: Github user
provider: github
scope: user
redirect-uri: http://localhost:${server.port}/login/oauth2/code/github
github-client-2:
client-id: ${APP-CLIENT-ID}
client-secret: ${APP-CLIENT-SECRET}
client-name: Github email
provider: github
scope: user:email
redirect-uri: http://localhost:${server.port}/login/oauth2/code/github
yahoo-oidc:
client-id: ${YAHOO-CLIENT-ID}
client-secret: ${YAHOO-CLIENT-SECRET}
github-repos:
client-id: ${APP-CLIENT-ID}
client-secret: ${APP-CLIENT-SECRET}
scope: public_repo
redirect-uri: "{baseUrl}/github-repos"
provider: github
client-name: GitHub Repositories
logging:
level:
# org.springframework.web: DEBUG
org.springframework.security: DEBUG
package cn.netkiller.oauth2.client;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
System.out.println("Oauth2 Client!");
SpringApplication.run(Application.class, args);
}
}
package cn.netkiller.oauth2.client;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().oauth2Login();
}
}
package cn.netkiller.oauth2.client;
import java.security.Principal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
private ClientRegistrationRepository clientRegistrationRepository;
@RequestMapping("/")
public Principal email(Principal principal) {
System.out.println("Hello " + principal.getName());
return principal;
}
@RequestMapping("/index")
public ClientRegistration index() {
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("netkiller");
return clientRegistration;
}
}
package cn.netkiller.okhttp.pojo;
public class Oauth {
private String access_token;
private String token_type;
private String refresh_token;
private String expires_in;
private String scope;
private String jti;
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
public String getToken_type() {
return token_type;
}
public void setToken_type(String token_type) {
this.token_type = token_type;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getExpires_in() {
return expires_in;
}
public void setExpires_in(String expires_in) {
this.expires_in = expires_in;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getJti() {
return jti;
}
public void setJti(String jti) {
this.jti = jti;
}
@Override
public String toString() {
return "Oauth{" +
"access_token='" + access_token + '\'' +
", token_type='" + token_type + '\'' +
", refresh_token='" + refresh_token + '\'' +
", expires_in='" + expires_in + '\'' +
", scope='" + scope + '\'' +
", jti='" + jti + '\'' +
'}';
}
}
Activity 文件
package cn.netkiller.okhttp;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import com.google.gson.Gson;
import java.io.IOException;
import cn.netkiller.okhttp.pojo.Oauth;
import okhttp3.Authenticator;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Credentials;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.Route;
public class Oauth2jwtActivity extends AppCompatActivity {
private TextView token;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_oauth2jwt);
token = (TextView) findViewById(R.id.token);
try {
get();
} catch (IOException e) {
e.printStackTrace();
}
}
public static Oauth accessToken() throws IOException {
OkHttpClient client = new OkHttpClient.Builder().authenticator(
new Authenticator() {
public Request authenticate(Route route, Response response) {
String credential = Credentials.basic("api", "secret");
return response.request().newBuilder().header("Authorization", credential).build();
}
}
).build();
String url = "https://api.netkiller.cn/oauth/token";
RequestBody formBody = new FormBody.Builder()
.add("grant_type", "password")
.add("username", "blockchain")
.add("password", "123456")
.build();
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
throw new IOException("服务器端错误: " + response);
}
Gson gson = new Gson();
Oauth oauth = gson.fromJson(response.body().string(), Oauth.class);
Log.i("oauth", oauth.toString());
return oauth;
}
public void get() throws IOException {
OkHttpClient client = new OkHttpClient.Builder().authenticator(
new Authenticator() {
public Request authenticate(Route route, Response response) throws IOException {
return response.request().newBuilder().header("Authorization", "Bearer " + accessToken().getAccess_token()).build();
}
}
).build();
String url = "https://api.netkiller.cn/misc/compatibility";
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
call.cancel();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String myResponse = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i("oauth", myResponse);
token.setText(myResponse);
}
});
}
});
}
}
<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>cn.netkiller</groupId> <artifactId>oauth</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>cn.netkiller</groupId> <artifactId>client</artifactId> <version>0.0.1-SNAPSHOT</version> <name>client</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.3</version> </dependency> </dependencies> </project>
package cn.netkiller.oauth.client;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Hello world!
*
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
System.out.println("Hello World!");
SpringApplication.run(Application.class, args);
}
}
package cn.netkiller.oauth.client.controller;
import org.apache.http.impl.client.DefaultHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;
@RestController
public class ClientRestController {
@RequestMapping("/token")
public String token() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization","Bearer "+"6296057a-ed06-4899-9adc-1993ba7a4946");
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<String> response = restTemplate.exchange("http://localhost:8000/api/test/hello.json",HttpMethod.GET,entity,String.class);
System.out.println(response.getBody());
return response.getStatusCode().toString() + " " + response.getBody();
}
}
首先获取 Token
MacBook-Pro:Application neo$ curl -X POST --user 'api:secret' -d 'grant_type=password&username=netkiller@msn.com&password=123456' http://localhost:8000/api/oauth/token
{"access_token":"6296057a-ed06-4899-9adc-1993ba7a4946","token_type":"bearer","refresh_token":"b22d70db-3253-4f5f-9b6a-8714da23e14d","expires_in":2642,"scope":"read write"}
将Token写入到 http 头
headers.set("Authorization","Bearer
"+"6296057a-ed06-4899-9adc-1993ba7a4946");
启动 client 项目
mvn spring-boot:run
curl 测试
MacBook-Pro:Application neo$ curl http://localhost:8080/client/token.json 200 Hello world !
package example.controller;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
@Controller
public class TestController {
@Autowired
private OAuth2RestOperations restTemplate;
@GetMapping("/ssl")
@ResponseBody
public String ssl() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
String url = "https://api.netkiller.cn/news/list.json";
SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(null, (chain, authType) -> true).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, new NoopHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
httpComponentsClientHttpRequestFactory.setConnectTimeout(60000);
httpComponentsClientHttpRequestFactory.setReadTimeout(180000);
final RestTemplate restTemplate = new RestTemplate(httpComponentsClientHttpRequestFactory);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + this.restTemplate.getAccessToken().getValue());
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
return response.getBody();
}
}
@RestController
public class ResourceController {
@GetMapping("/getResource")
public String getResource(Principal principal) {
return "SUCCESS, 当前用户:" + principal.getName();
}
@RequestMapping("/user")
public Principal user(Principal principal) {
return principal;
}
}
@RequestMapping(value = "principal", method = RequestMethod.GET)
public Object getPrincipal() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return principal;
}
// 查询用户角色
@RequestMapping(value = "roles", method = RequestMethod.GET)
public Object getRoles() {
return SecurityContextHolder.getContext().getAuthentication().getAuthorities();
}
ResourceServerConfigurerAdapter
@Override
public void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/home")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/user")
.access("#oauth2.hasScope('read')")
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
;
}
@Override
public void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/info","/news")
.access("#oauth2.hasScope('read') and hasRole('USER')");
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
TokenStore tokenStore = new JdbcTokenStore(dataSource);
resources.resourceId("user-services").tokenStore(tokenStore).stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.requestMatchers().antMatchers("/user/**")
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/user/**").access("#oauth2.hasScope('read')")
.antMatchers(HttpMethod.POST, "/user/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.PATCH, "/user/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.PUT, "/user/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.DELETE, "/user/**").access("#oauth2.hasScope('write')")
.and()
.headers().addHeaderWriter(new HeaderWriter() {
@Override
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
response.addHeader("Access-Control-Allow-Origin", "*");
if (request.getMethod().equals("OPTIONS")) {
response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method"));
response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
}
}
});
}
@Configuration
public class OAuth2LoginConfig {
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
return new InMemoryClientRegistrationRepository(this.googleClientRegistration());
}
private ClientRegistration googleClientRegistration() {
return ClientRegistration.withRegistrationId("google")
.clientId("google-client-id")
.clientSecret("google-client-secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUriTemplate("{baseUrl}/login/oauth2/code/{registrationId}")
.scope("openid", "profile", "email", "address", "phone")
.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")
.tokenUri("https://www.googleapis.com/oauth2/v4/token")
.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")
.userNameAttributeName(IdTokenClaimNames.SUB)
.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")
.clientName("Google")
.build();
}
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
configurer.authenticationManager(authenticationManager);
configurer.userDetailsService(userDetailsService);
configurer.pathMapping("/oauth/token","/oauth/token3"); //可以修改默认/oauth/token路径为 /oauth/token3
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
configurer.authenticationManager(authenticationManager);
configurer.userDetailsService(userDetailsService());
}
@Bean
public UserDetailsService userDetailsService() {
Map<String, String[]> users = new HashMap<String, String[]>() {
{
put("user", new String[] { "ROLE_USER" });
put("admin", new String[] { "ROLE_USER", "ROLE_ADMIN" });
put("client", new String[] { "ROLE_CLIENT" });
put("trust", new String[] { "ROLE_TRUSTED_CLIENT" });
}
};
String password = passwordEncoder().encode("123456"); // 设置默认密码
// TODO 这里需要自行定义访问数据库的扩展
return new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
String[] authList = users.containsKey(name) ? users.get(name) : new String[] { "ROLE_USER" };
User user = new User(name, password, AuthorityUtils.createAuthorityList(authList));
return user;
}
};
}