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
微服務系統的 認證 和 受權。
歡迎關注技術公衆號:零壹技術棧
本賬號將持續分享後端技術乾貨,包括虛擬機基礎,多線程編程,高性能框架,異步、緩存和消息中間件,分佈式和微服務,架構學習和進階等學習資料和文章。