一、搭建认证服务
1.maven 依赖
<groupId>com.example</groupId>
<artifactId>auth-center</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.配置
AuthorizationServer.java
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
private AuthenticationManager authenticationManager;
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
@Bean
public AuthorizationServerTokenServices tokenServices(){
DefaultTokenServices services = new DefaultTokenServices();
services.setClientDetailsService(clientDetailsService);
services.setSupportRefreshToken(true);
services.setTokenStore(tokenStore());
services.setAccessTokenValiditySeconds(7200);
services.setRefreshTokenValiditySeconds(259200);
return services;
}
@Bean
public AuthorizationCodeServices authorizationCodeServices(){
return new InMemoryAuthorizationCodeServices();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("c1")
.secret(new BCryptPasswordEncoder().encode("secret"))//$2a$10$0uhIO.ADUFv7OQ/kuwsC1.o3JYvnevt5y3qX/ji0AUXs4KYGio3q6
.resourceIds("r1")
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
.scopes("all")
.autoApprove(false)
.redirectUris("https://www.baidu.com");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.authorizationCodeServices(authorizationCodeServices)
.tokenServices(tokenServices())
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
}
}
WebSecurityConfig.java
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// 认证管理器
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
//密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 安全拦截机制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("zhangsan")
.password(passwordEncoder().encode("123"))
.authorities("p1", "p2")
.and()
.withUser("lisi")
.password(passwordEncoder().encode("456"))
.authorities("p1");
}
}
3.controller
IndexController.java
@RestController
public class IndexController {
@GetMapping("/index")
public String index() {
return "auth-center index";
}
@GetMapping("/hello")
public String hello() {
return "auth-center hello";
}
}
4.配置文件
application.yml
server:
port: 30000
spring:
application:
name: auth-center
5.启动类
@SpringBootApplication
public class AuthCenterApplication {
public static void main(String[] args) {
SpringApplication.run(AuthCenterApplication.class, args);
}
}
二、搭建资源服务
1.maven 依赖
<groupId>com.example</groupId>
<artifactId>resource-server</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.配置
ResouceServerConfig.java
@Configuration
@EnableResourceServer
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
@Bean
public ResourceServerTokenServices resourceServerTokenServices() {
RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setCheckTokenEndpointUrl("http://127.0.0.1:30000/oauth/check_token");
remoteTokenServices.setClientId("c1");
remoteTokenServices.setClientSecret("secret");
return remoteTokenServices;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("r1")
.tokenServices(resourceServerTokenServices()) // 令牌服务
.stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/r1").authenticated() // r1 需要认证
.anyRequest().permitAll(); // 其他请求则允许所有用户访问
}
}
3.controller
@RestController
public class IndexController {
@GetMapping("/index")
public String index() {
return "this is index";
}
@GetMapping("/r1")
public String r1(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return "this is r1";
}
}
4.配置文件
server:
port: 30001
spring:
application:
name: resource-server
5.启动类
@SpringBootApplication
public class ResourceServerApplication {
public static void main(String[] args) {
SpringApplication.run(ResourceServerApplication.class, args);
}
}
三、认证
1.授权码认证模式
最安全的一种模式。一般用于 client 是 Web 服务器端应用或第三方的原生 App 调用资源服务的时候。因为在这种模式中 access_token 不会经过浏览器或移动端的 App,而是直接从服务端去交换,这样就最大限度的减小了令牌泄漏的风险。 该模式下获取 token 需要分两步走,第一步获取授权码,第二步获取 token。
1) 获取 code
接口地址:http://127.0.0.1:30000/oauth/authorize
请求方式:GET
请求参数:
字段名 | 描述 |
---|---|
client_id | 改值必须和配置在 clients 中的值保持一致 |
response_type | 固定传值 code 表示使用授权码模式进行认证 |
scope | 改值必须配置的 clients 中的值一致 |
redirect_uri | 获取 code 之后重定向的地址,必须和配置的 clients 一致 |
请求示例:
http://127.0.0.1:30000/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=https://www.baidu.com
账号密码分别输入:zhangsan/123,进入授权页面之后点击授权按钮,页面跳转之后获取到 code
重定向到:
https://www.baidu.com/?code=rBWbw3
2) 获取 token
接口地址:http://127.0.0.1:30000/oauth/token
请求方式:POST
请求参数:
字段名 | 描述 |
---|---|
code | 上一步获取到的 code |
grant_type | 在授权码模式,固定使用 authorization_code |
client_id | 改值必须和配置在 clients 中的值保持一致 |
client_secret | 这里的值必须和代码中配置的 clients 中配置的保持一致 |
redirect_uri | 获取 token 之后重定向的地址,该地址可以随意写 |
请求示例:
zxm@zxm-pc:~$ curl -X POST "http://localhost:30000/oauth/token?code=rBWbw3&grant_type=authorization_code&client_id=c1&client_secret=secret&redirect_uri=https://www.baidu.com"
{"access_token":"992cdbf8-fe9c-47e3-ac0e-8ceb66a479c5","token_type":"bearer","refresh_token":"ee6e1589-1b5d-408a-a432-efa4f60d3da9","expires_in":7199,"scope":"all"}
2.简化模式
该模式去掉了授权码,用户登陆之后直接获取token并显示在浏览器地址栏中,参数和请求授权码的接口基本上一模一样,唯一的区别就是 response_type 字段,授权码模式下使用的是 code 字段,在简化模式下使用的是 token 字段。一般来说,简化模式用于没有服务器端的第三方单页面应用,因为没有服务器端就无法接收授权码。
接口地址:http://127.0.0.1:30000/oauth/authorize
请求方式:GET
请求参数:
字段名 | 描述 |
---|---|
client_id | 改值必须和配置在 clients 中的值保持一致 |
response_type | 固定传值 token 表示使用简化模式进行认证 |
scope | 该值必须和配置的 clients 中的值一致 |
redirect_uri | 获取 code 之后重定向的地址,必须和配置的 clients 一致 |
请求示例:
http://127.0.0.1:30000/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=https://www.baidu.com
重定向到:
https://www.baidu.com/#access_token=992cdbf8-fe9c-47e3-ac0e-8ceb66a479c5&token_type=bearer&expires_in=6521
3.密码模式
这种模式十分简单,但是却意味着直接将用户敏感信息泄漏给了 client,因此这就说明这种模式只能用于 client 是我 们自己开发的情况下。因此密码模式一般用于我们自己开发的,第一方原生App或第一方单页面应用。
接口地址:http://127.0.0.1:30000/oauth/token
请求方式:POST
请求参数:
字段名 | 描述 |
---|---|
client_id | 改值必须和配置在 clients 中的值保持一致 |
client_secret | 改值必须和配置在 clients 中的值保持一致 |
grant_type | 在密码模式下,该值固定为 password |
username | 用户名 |
password | 密码 |
请求示例:
zxm@zxm-pc:~$ curl -X POST "http://127.0.0.1:30000/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=zhangsan&password=123"
{"access_token":"992cdbf8-fe9c-47e3-ac0e-8ceb66a479c5","token_type":"bearer","refresh_token":"ee6e1589-1b5d-408a-a432-efa4f60d3da9","expires_in":6284,"scope":"all"}
4.客户端模式
这种模式是最方便但最不安全的模式。因此这就要求我们对client完全的信任,而client本身也是安全的。因此这种模式一般用来提供给我们完全信任的服务器端服务。比如,合作方系统对接,拉取一组用户信息。
接口地址:http://127.0.0.1:30000/oauth/token
请求方式:POST
请求参数:
| 字段名 | 描述 |
| client_id | 改值必须和配置在 clients 中的值保持一致 |
| client_secret | 改值必须和配置在 clients 中的值保持一致 |
| grant_type | 在密码模式下,该值固定为 client_credentials |
请求示例:
zxm@zxm-pc:~$ curl -X POST "http://127.0.0.1:30000/oauth/token?client_id=c1&client_secret=secret&grant_type=client_credentials"
{"access_token":"006912ba-ac7a-4ce0-84ec-e5c5a826ed17","token_type":"bearer","expires_in":7199,"scope":"all"}
5.refresh_token 换取新 token
接口地址:http://127.0.0.1:30000/oauth/token
请求方式:POST
请求参数:
字段名 | 描述 |
---|---|
client_id | 该值必须和配置在 clients 中的值保持一致 |
client_secret | 该值必须和配置在 clients 中的值保持一致 |
grant_type | 如果想根据 refresh_token 换新的 token,这里固定传值 refresh_token |
refresh_token | 未失效的r efresh_token |
请求示例:
zxm@zxm-pc:~$ curl -X POST "http://127.0.0.1:30000/oauth/token?grant_type=refresh_token&refresh_token=ee6e1589-1b5d-408a-a432-efa4f60d3da9&client_id=c1&client_secret=secret"
{"access_token":"f0e0e9ef-ea4e-480d-9b35-b8779c56f6c3","token_type":"bearer","refresh_token":"ee6e1589-1b5d-408a-a432-efa4f60d3da9","expires_in":7199,"scope":"all"}
四、使用
客户端请求
curl -X GET -H "Authorization: Bearer c5c7ed0c-cad7-4f37-8119-bd32cdea194b" "http://127.0.0.1:30001/r1"