示例代碼-githubhtml
客戶端模式(Client Credentials Grant)指客戶端以本身的名義,而不是以用戶的名義,向"服務提供商"進行認證。嚴格地說,客戶端模式並不屬於OAuth框架所要解決的問題。在這種模式中,用戶直接向客戶端註冊,客戶端以本身的名義要求"服務提供商"提供服務,其實不存在受權問題。java
(A)客戶端向認證服務器進行身份認證,並要求一個訪問令牌。客戶端發出的HTTP請求,包含如下參數: granttype:表示受權類型,此處的值固定爲"clientcredentials",必選項。 scope:表示權限範圍,可選項。mysql
(B)認證服務器確認無誤後,向客戶端提供訪問令牌。git
密碼模式(Resource Owner Password Credentials Grant)中,用戶向客戶端提供本身的用戶名和密碼。客戶端使用這些信息,向"服務商提供商"索要受權。 在這種模式中,用戶必須把本身的密碼給客戶端,可是客戶端不得儲存密碼。這一般用在用戶對客戶端高度信任的狀況下,好比客戶端是操做系統的一部分,或者由一個著名公司出品。而認證服務器只有在其餘受權模式沒法執行的狀況下,才能考慮使用這種模式。github
(A)用戶向客戶端提供用戶名和密碼。web
(B)客戶端將用戶名和密碼發給認證服務器,向後者請求令牌。 客戶端發出的HTTP請求,包含如下參數: grant_type:表示受權類型,此處的值固定爲"password",必選項。 username:表示用戶名,必選項。 password:表示用戶的密碼,必選項。 scope:表示權限範圍,可選項。redis
(C)認證服務器確認無誤後,向客戶端提供訪問令牌。spring
返回結果數據庫
{
"access_token": "67a2c8f6-bd08-4409-a0d6-6ba61a4be950",
"token_type": "bearer",
"expires_in": 41203,
"scope": "select"
}
複製代碼
返回結果
{
"access_token": "b3d2c131-1225-45b4-9ff5-51ec17511cee",
"token_type": "bearer",
"refresh_token": "8495d597-0560-4598-95ef-143c0855363c",
"expires_in": 42417,
"scope": "select"
}
複製代碼
返回結果
{
"access_token": "63de6c71-672f-418c-80eb-0c9abc95b67c",
"token_type": "bearer",
"refresh_token": "8495d597-0560-4598-95ef-143c0855363c",
"expires_in": 43199,
"scope": "select"
}
複製代碼
http://localhost:8080/order/1?access_token=b3d2c131-1225-45b4-9ff5-51ec17511cee
正確返回數據
security oauth2 整合的3個核心配置類
<dependencies>
<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>
<version>2.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.1</version>
<scope>test</scope>
</dependency>
</dependencies>
複製代碼
主要使用了:security、oauth二、redis、mysql、mybatis-plus等組件
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private static final String RESOURCE_IDS = "order";
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
String finalSecret = "{bcrypt}" + new BCryptPasswordEncoder().encode("123456");
//配置兩個客戶端,一個用於password認證一個用於client認證
clients.inMemory()
//client模式
.withClient("client_1")
.resourceIds(RESOURCE_IDS)
.authorizedGrantTypes("client_credentials", "refresh_token")
.scopes("select")
.authorities("oauth2")
.secret(finalSecret)
.and()
//密碼模式
.withClient("client_2")
.resourceIds(RESOURCE_IDS)
.authorizedGrantTypes("password", "refresh_token")
.scopes("select")
.authorities("oauth2")
.secret(finalSecret);
}
/** * 認證服務端點配置 */
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
//用戶管理
.userDetailsService(userDetailsService)
//token存到redis
.tokenStore(new RedisTokenStore(redisConnectionFactory))
//啓用oauth2管理
.authenticationManager(authenticationManager)
//接收GET和POST
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.allowFormAuthenticationForClients();
}
}
複製代碼
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_IDS = "order";
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_IDS).stateless(true);
}
@Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests()
.antMatchers("/order/**").authenticated(); //配置order訪問控制,必須認證事後才能夠訪問
}
}
複製代碼
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserServiceImpl userService;
/** * 實現UserDetailsService中的loadUserByUsername方法,用於加載用戶數據 */
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.queryUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用戶不存在");
}
//用戶權限列表
Collection<? extends GrantedAuthority> authorities = userService.queryUserAuthorities(user.getId());
return new AuthUser(
user.getId(),
user.getUsername(),
user.getPassword(),
true,
true,
true,
true,
authorities);
}
}
複製代碼
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
/** * 注入AuthenticationManager接口,啓用OAuth2密碼模式 * * @return * @throws Exception */
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
/** * 經過HttpSecurity實現Security的自定義過濾配置 * * @param httpSecurity * @throws Exception */
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.requestMatchers().anyRequest()
.and()
.authorizeRequests()
.antMatchers("/oauth/**").permitAll();
}
}
複製代碼
server:
port: 8080
spring:
thymeleaf:
encoding: UTF-8
cache: false
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/easy_web?useSSL=false&serverTimezone=UTC
username: root
password: 123456
redis:
host: 127.0.0.1
port: 6379
password:
logging.level.org.springframework.security: DEBUG
複製代碼
DROP TABLE IF EXISTS `user`;
DROP TABLE IF EXISTS `role`;
DROP TABLE IF EXISTS `user_role`;
DROP TABLE IF EXISTS `role_permission`;
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `user` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `role` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `user_role` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(11) NOT NULL,
`role_id` bigint(11) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `role_permission` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`role_id` bigint(11) NOT NULL,
`permission_id` bigint(11) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `permission` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`url` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`description` varchar(255) NULL,
`pid` bigint(11) NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO user (id, username, password) VALUES (1,'user','{bcrypt}$2a$10$Tme77eHtXzcB8ghQUepYguJr7P7ESg0Y7XHMnk60s.kf2A.BWBD9m');
INSERT INTO user (id, username , password) VALUES (2,'admin','{bcrypt}$2a$10$Tme77eHtXzcB8ghQUepYguJr7P7ESg0Y7XHMnk60s.kf2A.BWBD9m');
INSERT INTO role (id, name) VALUES (1,'USER');
INSERT INTO role (id, name) VALUES (2,'ADMIN');
INSERT INTO permission (id, url, name, pid) VALUES (1,'/user/common','common',0);
INSERT INTO permission (id, url, name, pid) VALUES (2,'/user/admin','admin',0);
INSERT INTO user_role (user_id, role_id) VALUES (1, 1);
INSERT INTO user_role (user_id, role_id) VALUES (2, 1);
INSERT INTO user_role (user_id, role_id) VALUES (2, 2);
INSERT INTO role_permission (role_id, permission_id) VALUES (1, 1);
INSERT INTO role_permission (role_id, permission_id) VALUES (2, 1);
INSERT INTO role_permission (role_id, permission_id) VALUES (2, 2);
複製代碼
通過以上七個步驟,咱們快速實現了Oauth2的密碼模式和客戶模式功能。