OAuth 2 有四种授权模式,分别是授权码模式(authorization code)、简化模式(implicit)、密码模式(resource owner password credentials)、客户端模式(client credentials),具体 OAuth2 是什么,可以参考这篇文章(http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html)
————————————————
上篇讲解的是授权码模式,本篇主要讲解密码模式。平常主要使用的就是这两种模式。(注:本篇基于上一篇的代码基础之上)
1.客户端带着clientid,screteid,用户名,密码。到授权服务器获取token。
2.客户端获取到token之后,带着token访问服务。
3.服务获取客户端带过来的token,到授权服务器进行验证token的合法性。有效允许访问服务,无效反之。就是如此简单。
到这。我们开发人员需要实现的就第一,第二步。第三步由我们配置验证url,Spring Cloud OAuth2框架会自动帮我们完成验证。
DemoApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
ResourceServerConfig.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.RemoteTokenServices;
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;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
/**
* Created by Administrator on 2020-06-12.
*/
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
// @Value("${security.oauth2.client.client-id}")
// private String clientId;
//
// @Value("${security.oauth2.client.client-secret}")
// private String secret;
//
// @Value("${security.oauth2.authorization.check-token-access}")
// private String checkTokenEndpointUrl;
//
// @Autowired
// private RedisConnectionFactory redisConnectionFactory;
//
// @Bean
// public TokenStore redisTokenStore (){
// return new RedisTokenStore(redisConnectionFactory);
// }
//
// @Bean
// public RemoteTokenServices tokenService() {
// RemoteTokenServices tokenService = new RemoteTokenServices();
// tokenService.setClientId(clientId);
// tokenService.setClientSecret(secret);
// tokenService.setCheckTokenEndpointUrl(checkTokenEndpointUrl);
// return tokenService;
// }
//
// @Override
// public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
// resources.tokenServices(tokenService());
// }
@Bean
public TokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey("dev");
accessTokenConverter.setVerifierKey("dev");
return accessTokenConverter;
}
@Autowired
private TokenStore jwtTokenStore;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(jwtTokenStore);
}
}
UserController.java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.nio.charset.StandardCharsets;
/**
* Created by Administrator on 2020-06-12.
*/
@RestController
public class UserController {
@GetMapping(value = "get")
//@PreAuthorize("hasAuthority('ROLE_ADMIN')")
@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
public Object get(Authentication authentication){
//Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
authentication.getCredentials();
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)authentication.getDetails();
String token = details.getTokenValue();
return token;
}
@GetMapping(value = "jwt")
@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
public Object jwtParser(Authentication authentication){
authentication.getCredentials();
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)authentication.getDetails();
String jwtToken = details.getTokenValue();
Claims claims = Jwts.parser()
.setSigningKey("dev".getBytes(StandardCharsets.UTF_8))
.parseClaimsJws(jwtToken)
.getBody();
return claims;
}
}
application.properties
spring.application.name=client-user
server.port=6101
server.servlet.context-path=/client-user
spring.redis.database=2
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=0
spring.redis.timeout=100ms
security.oauth2.client.client-id=user-client
security.oauth2.client.client-secret=user-secret-8888
security.oauth2.client.user-authorization-uri=http://localhost:6001/oauth/authorize
security.oauth2.client.access-token-uri=http://localhost:6001/oauth/token
#security.oauth2.resource.id=user-client
#security.oauth2.resource.user-info-uri=user-info
security.oauth2.resource.jwt.key-uri=http://localhost:6001/oauth/token_key
security.oauth2.resource.jwt.key-value=dev
security.oauth2.authorization.check-token-access=http://localhost:6001/oauth/check_token
eureka.client.service-url.defaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/
pom.xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.0.0.RELEASE
com.example
demo
0.0.1-SNAPSHOT
demo
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-oauth2
org.springframework.boot
spring-boot-starter-data-redis
io.jsonwebtoken
jjwt
0.9.1
org.springframework.cloud
spring-cloud-dependencies
Finchley.RELEASE
pom
import
org.springframework.boot
spring-boot-maven-plugin
以上便完成了代码部分。下面进行测试。测试工具postman。
1启动注册中心,认证服务,client-user。
1.获取token:在postman中输入http://localhost:6001/oauth/token?grant_type=password&username=admin&password=123456&scope=all
假设咱们在一个 web 端使用,grant_type 是 password,表明这是使用 OAuth2 的密码模式。
username=admin 和 password=123456 就相当于在 web 端登录界面输入的用户名和密码,我们在认证服务端配置中固定了用户名是 admin 、密码是 123456,而线上环境中则应该通过查询数据库获取。
scope=all 是权限有关的,在认证服务的 OAuthConfig 中指定了 scope 为 all 。
Authorization 要加在请求头中,格式为 Basic 空格 base64(clientId:clientSecret),这个微服务客户端的 client-id 是 user-client,client-secret 是 user-secret-8888,将这两个值通过冒号连接,并使用 base64 编码(user-client:user-secret-8888)之后的值为 dXNlci1jbGllbnQ6dXNlci1zZWNyZXQtODg4OA==,可以通过 https://www.sojson.com/base64.html 在线编码获取。
token返回到客户端。
2.通过token访问服务:我们在用户客户端中定义了一个接口 http://localhost:6101/client-user/get,现在就拿着上一步获取的 token 来请求这个接口。
同样需要请求头 Authorization,格式为 bearer + 空格 + token,正常情况下根据接口的逻辑,会把 token 原样返回
3.刷新token: http://localhost:6001/oauth/token?grant_type=refresh_token&refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiJjNWMyYTdlMC1mYzc3LTRiN2ItYWE5Ni0wNzA5YmU5ZmZjZjQiLCJleHAiOjE1OTU2NDYwODgsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiZTYyMDgxNTctN2JkYy00MjBhLTkyMTctZTA0MDUzNzZjMDJhIiwiY2xpZW50X2lkIjoidXNlci1jbGllbnQifQ.gaNQF4UEjti_TZwJJaHAM8b7pd6JeeAysHre4jPbuxA
通过接口带着上一步获取的refresh_token.结果如下