OAuth 2.0 是介于 用户资源 和 第三方应用 之间的一个 中间层 ,它把 资源 和 第三方应用 隔开,使得 第三方应用 无法直接访问 资源 ,从而起到 保护资源 的作用。为了访问这种 受限资源 , 第三方应用 (客户端)在访问的时候需要 提供凭证 。
在 认证 与 授权 的过程中,主要包含以下 3 种角色:
服务提供方: Authorization Server
资源持有者: Resource Server
客户端: Client
OAuth 2.0 的 认证流程 如图所示,具体如下:
用户( 资源持有者 )打开 客户端 , 客户端 询问 用户授权 。
用户同意授权。
客户端向 授权服务器 申请授权。
授权服务器对 客户端 进行认证,也包括 用户信息 的认证,认证成功后授权给予 令牌 。
客户端获取令牌后, 携带令牌 向 资源服务器 请求资源。
资源服务器确认令牌正确无误,向 客户端 发放资源。
OAuth2 Provider 的角色被分为 Authorization Server ( 授权服务 )和 Resource Service ( 资源服务 ),通常它们不在同一个服务中,可能一个 Authorization Service 对应 多个 Resource Service 。 Spring OAuth2.0 需配合 Spring Security 一起使用,所有的请求由 Spring MVC 控制器处理,并经过一系列的 Spring Security 过滤器拦截。
在 Spring Security 过滤器链 中有以下两个 端点 ,这两个节点用于从 Authorization Service 获取验证 和 授权 。
用于 授权 的端点:默认为 /oauth/authorize 。
用于获取 令牌 的端点:默认为 /oauth/token 。
客户端信息可以存储在 数据库 中,这样就可以通过更改 数据库 来实时 更新客户端信息 的数据。 Spring OAuth2 已经设计好了数据库的表,且不可变。首先将以下 DDL 导入数据库中。
SET NAMES utf8; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for `clientdetails` -- ---------------------------- DROP TABLE IF EXISTS `clientdetails`; CREATE TABLE `clientdetails` ( `appId` varchar(128) NOT NULL, `resourceIds` varchar(256) DEFAULT NULL, `appSecret` varchar(256) DEFAULT NULL, `scope` varchar(256) DEFAULT NULL, `grantTypes` varchar(256) DEFAULT NULL, `redirectUrl` varchar(256) DEFAULT NULL, `authorities` varchar(256) DEFAULT NULL, `access_token_validity` int(11) DEFAULT NULL, `refresh_token_validity` int(11) DEFAULT NULL, `additionalInformation` varchar(4096) DEFAULT NULL, `autoApproveScopes` varchar(256) DEFAULT NULL, PRIMARY KEY (`appId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for `oauth_access_token` -- ---------------------------- DROP TABLE IF EXISTS `oauth_access_token`; CREATE TABLE `oauth_access_token` ( `token_id` varchar(256) DEFAULT NULL, `token` blob, `authentication_id` varchar(128) NOT NULL, `user_name` varchar(256) DEFAULT NULL, `client_id` varchar(256) DEFAULT NULL, `authentication` blob, `refresh_token` varchar(256) DEFAULT NULL, PRIMARY KEY (`authentication_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for `oauth_approvals` -- ---------------------------- DROP TABLE IF EXISTS `oauth_approvals`; CREATE TABLE `oauth_approvals` ( `userId` varchar(256) DEFAULT NULL, `clientId` varchar(256) DEFAULT NULL, `scope` varchar(256) DEFAULT NULL, `status` varchar(10) DEFAULT NULL, `expiresAt` datetime DEFAULT NULL, `lastModifiedAt` datetime DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for `oauth_client_details` -- ---------------------------- DROP TABLE IF EXISTS `oauth_client_details`; CREATE TABLE `oauth_client_details` ( `client_id` varchar(256) NOT NULL, `resource_ids` varchar(256) DEFAULT NULL, `client_secret` varchar(256) DEFAULT NULL, `scope` varchar(256) DEFAULT NULL, `authorized_grant_types` varchar(256) DEFAULT NULL, `web_server_redirect_uri` varchar(256) DEFAULT NULL, `authorities` varchar(256) DEFAULT NULL, `access_token_validity` int(11) DEFAULT NULL, `refresh_token_validity` int(11) DEFAULT NULL, `additional_information` varchar(4096) DEFAULT NULL, `autoapprove` varchar(256) DEFAULT NULL, PRIMARY KEY (`client_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for `oauth_client_token` -- ---------------------------- DROP TABLE IF EXISTS `oauth_client_token`; CREATE TABLE `oauth_client_token` ( `token_id` varchar(256) DEFAULT NULL, `token` blob, `authentication_id` varchar(128) NOT NULL, `user_name` varchar(256) DEFAULT NULL, `client_id` varchar(256) DEFAULT NULL, PRIMARY KEY (`authentication_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for `oauth_code` -- ---------------------------- DROP TABLE IF EXISTS `oauth_code`; CREATE TABLE `oauth_code` ( `code` varchar(256) DEFAULT NULL, `authentication` blob ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for `oauth_refresh_token` -- ---------------------------- DROP TABLE IF EXISTS `oauth_refresh_token`; CREATE TABLE `oauth_refresh_token` ( `token_id` varchar(256) DEFAULT NULL, `token` blob, `authentication` blob ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for `role` -- ---------------------------- DROP TABLE IF EXISTS `role`; CREATE TABLE `role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for `user` -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `password` varchar(255) DEFAULT NULL, `username` varchar(255) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `UK_sb8bbouer5wak8vyiiy4pf2bx` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for `user_role` -- ---------------------------- DROP TABLE IF EXISTS `user_role`; CREATE TABLE `user_role` ( `user_id` bigint(20) NOT NULL, `role_id` bigint(20) NOT NULL, KEY `FKa68196081fvovjhkek5m97n3y` (`role_id`), KEY `FK859n2jvi8ivhui0rl0esws6o` (`user_id`), CONSTRAINT `FK859n2jvi8ivhui0rl0esws6o` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`), CONSTRAINT `FKa68196081fvovjhkek5m97n3y` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; SET FOREIGN_KEY_CHECKS = 1; 复制代码
采用 Maven 的多 Module 的项目结构,新建一个 空白的 Maven 工程,并在 根目录 的 pom.xml 文件中配置 Spring Boot 的版本 1.5.3.RELEASE , Spring Cloud 的版本为 Dalston.RELEASE ,完整的代码如下:
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/>
</parent>
<modules>
<module>eureka-server</module>
<module>service-auth</module>
<module>service-hi</module>
</modules>
<groupId>io.github.ostenant.springcloud</groupId>
<artifactId>spring-cloud-oauth2-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-oauth2-example</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Dalston.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
复制代码
新建一个 eureka-server 模块,并添加 Eureka 的相关依赖,并指定 pom.xml 的父节点如下:
<?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>io.github.ostenant.springcloud</groupId>
<artifactId>eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>eureka-server</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>io.github.ostenant.springcloud</groupId>
<artifactId>spring-cloud-oauth2-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
复制代码
在 eureka-server 模块的配置文件 application.yml 中配置 Eureka Server 的信息:
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
复制代码
最后在应用的 启动类 上添加 @EnableEurekaServer 注解开启 Eureka Server 的功能。
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
复制代码
新建一个 service-auth 模块,并添加以下依赖,作为 Uaa ( 授权服务 ),完整的代码如下:
<?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>io.github.ostenant.springcloud</groupId>
<artifactId>service-auth</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>service-auth</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>io.github.ostenant.springcloud</groupId>
<artifactId>spring-cloud-oauth2-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
复制代码
打开 spring-cloud-starter-oauth2 依赖包可以看到,它已经整合了以下 3 个 起步依赖 :
spring-cloud-starter-security
spring-security-oauth2
spring-security-jwt
在 service-oauth 模块中的 application.yml 完成如下配置:
spring:
application:
name: service-auth
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/spring-cloud-auth?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update
show-sql: true
server:
context-path: /uaa
port: 5000
security:
oauth2:
resource:
filter-order: 3
# basic:
# enabled: false
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
复制代码
配置 security.oauth2.resource.filter-order 为 3 ,在 Spring Boot 1.5.x 版本之前,可以省略此配置。
由于 auth-service 需要对外暴露检查 Token 的 API 接口,所以 auth-service 其实也是一个 资源服务 ,需要在 auth-service 中引入 Spring Security ,并完成相关配置,从而对 auth-service 的 资源 进行保护。
WebSecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserServiceDetail userServiceDetail;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.authorizeRequests().anyRequest().authenticated()
.and()
.csrf().disable();
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userServiceDetail).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
public @Bean AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
复制代码
UserServiceDetail.java
@Service
public class UserServiceDetail implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username);
}
}
复制代码
配置表的关系映射类 User ,需要实现 UserDetails 接口:
@Entity
public class User implements UserDetails, Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column
private String password;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
private List<Role> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
// setter getter
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
复制代码
配置表的关系映射类 Role ,需要实现 GrantedAuthority 接口:
@Entity
public class Role implements GrantedAuthority {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
// setter getter
@Override
public String getAuthority() {
return name;
}
@Override
public String toString() {
return name;
}
}
复制代码
UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
复制代码
配置 认证服务器 ,使用 @EnableAuthorizationServer 注解开启 Authorization Server ,对外提供 认证 和 授权 的功能。
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
// 将Token存储在内存中
// private TokenStore tokenStore = new InMemoryTokenStore();
private TokenStore tokenStore = new JdbcTokenStore(dataSource);
@Autowired
@Qualifier("dataSource")
private DataSource dataSource;
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Autowired
private UserServiceDetail userServiceDetail;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 将客户端的信息存储在内存中
clients.inMemory()
// 创建了一个client名为browser的客户端
.withClient("browser")
// 配置验证类型
.authorizedGrantTypes("refresh_token", "password")
// 配置客户端域为“ui”
.scopes("ui")
.and()
.withClient("service-hi")
.secret("123456")
.authorizedGrantTypes("client_credentials", "refresh_token","password")
.scopes("server");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 配置Token的存储方式
endpoints.tokenStore(tokenStore)
// 注入WebSecurityConfig配置的bean
.authenticationManager(authenticationManager)
// 读取用户的验证信息
.userDetailsService(userServiceDetail);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
// 对获取Token的请求不再拦截
oauthServer.tokenKeyAccess("permitAll()")
// 验证获取Token的验证信息
.checkTokenAccess("isAuthenticated()");
}
}
复制代码
在应用的启动类上,使用 @EnableResourceServer 注解 开启资源服务 ,应用需要对外暴露获取 token 的 API 接口。
@EnableEurekaClient
@EnableResourceServer
@SpringBootApplication
public class ServiceAuthApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceAuthApplication.class, args);
}
}
复制代码
本例采用 RemoteTokenService 这种方式对 token 进行 验证 。如果 其他资源服务 需要验证 token ,则需要远程调用 授权服务 暴露的 验证 token 的 API 接口。
@RestController
@RequestMapping("/users")
public class UserController {
@RequestMapping(value = "/current", method = RequestMethod.GET)
public Principal getUser(Principal principal) {
return principal;
}
}
复制代码
新建一个 service-hi 模块,这个服务作为 资源服务 。在 pom.xml 文件引入如下依赖:
<?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>io.github.ostenant.springcloud</groupId>
<artifactId>service-hi</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>service-hi</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>io.github.ostenant.springcloud</groupId>
<artifactId>spring-cloud-oauth2-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
复制代码
在 application.yml 中配置 service-hi 在 service-auth 中配置的 OAuth Client 信息:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8762
spring:
application:
name: service-hi
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/spring-cloud-auth?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update
show-sql: true
security:
oauth2:
resource:
user-info-uri: http://localhost:5000/uaa/users/current #获取当前Token的用户信息
client:
clientId: service-hi
clientSecret: 123456
accessTokenUri: http://localhost:5000/uaa/oauth/token #获取Token
grant-type: client_credentials,password
scope: server
复制代码
server-hi 模块作为 Resource Server ( 资源服务 ),需要进行 Resource Server 的相关配置,配置代码如下:
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 对用户注册的URL地址开放
.antMatchers("/user/registry").permitAll()
.anyRequest().authenticated();
}
}
复制代码
@Configuration
@EnableOAuth2Client
@EnableConfigurationProperties
public class OAuth2ClientConfig {
@Bean
@ConfigurationProperties(prefix = "security.oauth2.client")
public ClientCredentialsResourceDetails clientCredentialsResourceDetails() {
// 配置受保护资源的信息
return new ClientCredentialsResourceDetails();
}
@Bean
public RequestInterceptor oauth2FeignRequestInterceptor(){
// 配置一个拦截器,对于每一个外来的请求,都会在request域内创建一个AccessTokenRequest类型的bean。
return new OAuth2FeignRequestInterceptor(
new DefaultOAuth2ClientContext(),
clientCredentialsResourceDetails());
}
@Bean
public OAuth2RestTemplate clientCredentialsRestTemplate() {
// 用于向认证服务器服务请求token
return new OAuth2RestTemplate(clientCredentialsResourceDetails());
}
}
复制代码
把 service-auth 模块的 User.java 和 UserRepository.java 拷贝到 service-hi 模块中。创建 UserService 用于 创建用户 ,并对 用户密码 进行 加密 。
UserService.java
@Service
public class UserService {
private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
@Autowired
private UserRepository userRepository;
public User create(String username, String password) {
User user = new User();
user.setUsername(username);
String hash = encoder.encode(password);
user.setPassword(hash);
return userRepository.save(user);
}
}
复制代码
UserController.java
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/registry", method = RequestMethod.POST)
public User createUser(@RequestParam("username") String username,
@RequestParam("password") String password) {
return userService.create(username,password);
}
}
复制代码
@RestController
public class HiController {
private static final Logger LOGGER = LoggerFactory.getLogger(HiController.class);
@Value("${server.port}")
private String port;
/**
* 不需要任何权限,只要Header中的Token正确即可
*/
@RequestMapping("/hi")
public String hi() {
return "hi : " + ",i am from port: " + port;
}
/**
* 需要ROLE_ADMIN权限
*/
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
@RequestMapping("/hello")
public String hello() {
return "hello you!";
}
/**
* 获取当前认证用户的信息
*/
@GetMapping("/getPrinciple")
public OAuth2Authentication getPrinciple(OAuth2Authentication oAuth2Authentication,
Principal principal,
Authentication authentication){
LOGGER.info(oAuth2Authentication.getUserAuthentication().getAuthorities().toString());
LOGGER.info(oAuth2Authentication.toString());
LOGGER.info("principal.toString()" + principal.toString());
LOGGER.info("principal.getName()" + principal.getName());
LOGGER.info("authentication:" + authentication.getAuthorities().toString());
return oAuth2Authentication;
}
}
复制代码
@EnableEurekaClient
@SpringBootApplication
public class ServiceHiApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceHiApplication.class, args);
}
}
复制代码
依次启动 eureka-service , service-auth 和 service-hi 三个服务。
token
/hi ,不需要权限,只要 token 正确即可
/hello ,提示需要 ROLE_ADMIN 权限
role 表,添加 权限信息 ROLE_ADMIN ,然后在 user_role 表关联下再次访问
本案列架构有仍有改进之处。例如在 资源服务器 加一个 登录接口 ,该接口不受 Spring Security 保护。登录成功后, service-hi 远程调用 auth-service 获取 token 返回给浏览器,浏览器以后所有的请求都需要携带该 token 。
这个架构的缺陷就是, 每次请求 都需要由 资源服务 内部 远程调用 service-auth 服务来 验证 token 的正确性,以及该 token 对应的用户所具有的 权限 ,多了一次额外的 内部请求开销 。如果在 高并发 的情况下, service-auth 需要以 集群 的方式部署,并且需要做 缓存处理 。所以最佳方案还是结合 Spring Security OAuth2 和 JWT 一起使用,来实现 Spring Cloud 微服务系统的 认证 和 授权 。