社區羣聊
添加小編微信進入java學習交流羣,小編微信:372787553,備註進羣
本文是基於數據庫實現的,如果您想基於Redis實現,請移步到SpringCloud+OAuth2 統一權限驗證
OAuth2簡介
OAuth2.0是OAuth協議的延續版本,但不向後兼容OAuth 2.0即完全廢止了OAuth1.0。 OAuth
2.0關注客戶端開發者的簡易性。要麼通過組織在資源擁有者和HTTP服務商之間的被批准的交互動作代表用戶,要麼允許第三方應用代表用戶獲得訪問的權限
關鍵名詞
在詳細講解OAuth 2.0之前,需要了解幾個專用名詞。它們對讀懂後面的講解,尤其是幾張圖,至關重要。
(1) Third-party application:第三方應用程序,
(2)HTTP service:HTTP服務提供商,本文中簡稱"服務提供商",
(3)Resource Owner:資源所有者
(4)User Agent:用戶代理,本文中就是指瀏覽器。
(5)Authorization server:認證服務器,即服務提供商專門用來處理認證的服務器
(6)Resource server:資源服務器,即服務提供商存放用戶生成的資源的服務器。它與認證服務器,可以是同一臺服務器,也可以是不同的服務器。
知道了上面這些名詞,就不難理解,OAuth的作用就是讓"客戶端"安全可控地獲取"用戶"的授權,與"服務商提供商"進行互動。
以上介紹節選自上一篇文章SpringCloud+OAuth2 統一權限驗證https://blog.csdn.net/weixin_38937840/article/details/90321037
這篇文章使用的是Redis存儲token,本文我們將介紹使用JDBC的方式存儲token
搭建項目
創建服務
javayh-server是統一認證服務器;
javayh-resource是資源服務器;
核心依賴
由於篇幅較長,這裏我只列出了一部分,文章末尾會有源碼地址
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- druid 官方 starter -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.version}</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelpe.version}</version>
</dependency>
數據庫腳本
由於SQL腳本所佔的篇幅過長,我放在了另一個文檔裏,點擊查看
初始化SQL https://blog.csdn.net/weixin_38937840/article/details/105382901
認證服務器配置
以上的準備工作都完成了,我們就可以配置認證服務器了;
配置統一認證處理
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration
extends AuthorizationServerConfigurerAdapter {
@Autowired(required = false)
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
@Autowired
@Qualifier("userDetailsServiceImpl")
private UserDetailsService userDetailsService;
/**
* 自定義獲取客戶端,爲了支持自定義權限,
*/
@Bean
public ClientDetailsService clientDetails() {
JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(
dataSource);
jdbcClientDetailsService.setPasswordEncoder(new BCryptPasswordEncoder());
return jdbcClientDetailsService;
}
/**
* 令牌存放
* @return
*/
@Bean
public TokenStore tokenStore() {
// 基於 JDBC 實現,令牌保存到數據
return new JdbcTokenStore(dataSource);
}
/**
* 授權store
* @return
*/
@Bean
public ApprovalStore approvalStore() {
return new JdbcApprovalStore(dataSource);
}
/**
* 令牌信息拓展
* @return
*/
@Bean
public TokenEnhancer tokenEnhancer() {
return new BaseTokenEnhancer();
}
/**
* 授權碼
* @return
*/
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
/**
* 配置客戶端詳細信息服務
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
clients.withClientDetails(clientDetails());
}
/**
* 令牌訪問端點
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
// 允許post提交
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
// 密碼模式需要
.authenticationManager(authenticationManager)
.approvalStore(approvalStore()).userDetailsService(userDetailsService)
.tokenServices(createDefaultTokenServices())
.accessTokenConverter(SecurityHelper.buildAccessTokenConverter())
// 設置令牌存儲在數據庫
.tokenStore(tokenStore())
.authorizationCodeServices(authorizationCodeServices());
}
private DefaultTokenServices createDefaultTokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
// 令牌存儲策略
tokenServices.setTokenStore(tokenStore());
tokenServices.setTokenEnhancer(tokenEnhancer());
// 是否產生刷新令牌
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(true);
tokenServices.setClientDetailsService(clientDetails());
return tokenServices;
}
/**
* 令牌訪問端點安全策略
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
security
// 開啓/oauth/check_token驗證端口認證權限訪問
.checkTokenAccess("isAuthenticated()")
// 開啓表單認證,申請令牌
.allowFormAuthenticationForClients()
// 允許表單驗證
.checkTokenAccess("permitAll()");
}
}
服務器安全配置
package com.javayh.oauth2.server.conf;
import com.javayh.oauth2.server.conf.service.UserDetailsServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* <p>
* 授權回調
* </p>
* @version 1.0.0
* @author Dylan-haiji
* @since 2020/4/8
*/
@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
// 設置默認的加密方式
return new BCryptPasswordEncoder();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 使用自定義認證與授權
auth.userDetailsService(userDetailsService());
}
@Override
public void configure(WebSecurity web) throws Exception {
// 將 check_token 暴露出去,否則資源服務器訪問時報 403 錯誤
web.ignoring().antMatchers("/oauth/check_token");
}
}
自定義權限配置
package com.javayh.oauth2.server.conf.service;
import com.google.common.collect.Lists;
import com.javayh.oauth.domain.TbPermission;
import com.javayh.oauth.domain.TbUser;
import com.javayh.oauth2.server.service.TbPermissionService;
import com.javayh.oauth2.server.service.TbUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 自定義用戶認證與授權
* </p>
*
* @version 1.0.0
* @author Dylan-haiji
* @since 2020/4/5
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private TbUserService tbUserService;
@Autowired
private TbPermissionService tbPermissionService;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
TbUser tbUser = tbUserService.getByUsername(username);
List<GrantedAuthority> grantedAuthorities = Lists.newArrayList();
if (tbUser != null) {
// 獲取用戶授權
List<TbPermission> tbPermissions = tbPermissionService
.selectByUserId(tbUser.getId());
// 聲明用戶授權
tbPermissions.forEach(tbPermission -> {
if (tbPermission != null && tbPermission.getEnname() != null) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(
tbPermission.getEnname());
grantedAuthorities.add(grantedAuthority);
}
});
}
return new User(tbUser.getUsername(), tbUser.getPassword(), grantedAuthorities);
}
}
到這授權服務器配置完成,關於配置文件,請參考github配置文件 https://github.com/Dylan-haiji/javayh-platform/tree/master/javayh-sso/javayh-server/src/main/resources
資源服務器配置
資源服務器配置
package com.javayh.resource.conf;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
/**
* <p>
* 資源服務器配置
* </p>
* @version 1.0.0
* @author Dylan-haiji
* @since 2020/4/8
*/
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http
.antMatcher("/**").authorizeRequests();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and().authorizeRequests()
// 指定監控訪問權限
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll().anyRequest()
.authenticated().and().exceptionHandling()
// 認證鑑權錯誤處理,爲了統一異常處理。每個資源服務器都應該加上。
// .accessDeniedHandler(new AccessDeniedHandler())
// .authenticationEntryPoint(new WebException())
// 配置跨域
.and().cors().and().csrf().disable()
// 禁用httpBasic
.httpBasic().disable();
registry.anyRequest().authenticated();
}
}
配置文件
security:
oauth2:
client:
client-id: client
client-secret: secret
access-token-uri: http://localhost:9090/oauth/token
user-authorization-uri: http://localhost:9090/oauth/authorize
resource:
token-info-uri: http://localhost:9090/oauth/check_token
調用流程
獲取token
- 啓動服務服務獲取code
訪問:http://localhost:9090/oauth/authorize?client_id=client&response_type=code
- 初次需要登錄,如下圖
登錄名:admin , 密碼:123456
-
獲取code
-
獲取token
這裏我們使用postman獲取token
訪問:http://client:secret@localhost:9090/oauth/token
如下圖:
詳細步驟:https://github.com/Dylan-haiji/javayh-platform/tree/master/javayh-sso/javayh-server
根據token獲取資源
- 訪問資源
http://localhost:9099/api/user/info
這時有兩種方式訪問
-
access_toke
-
Authorization
headers攜帶
詳細步驟:https://github.com/Dylan-haiji/javayh-platform/tree/master/javayh-sso/javayh-resource
本問全部源碼以放在github上,歡迎點贊,或者加入開發,也可加入社區交流羣,添加小編微信:372787553
地址:https://github.com/Dylan-haiji/javayh-platform/tree/master/javayh-sso
本文的分享暫時就到這裏,希望對您有所幫助
關注 Java有貨領取更多資料
聯繫小編。微信:372787553,帶您進羣互相學習
左側小編微信,右側獲取免費資料
- SpringCloud 自定義封裝架構https://github.com/yanghaiji/javayh-platform
- Java 設計模式學習代碼 https://github.com/yanghaiji/design-pattern
- SpringCloud學習代碼: https://github.com/yanghaiji/javayh-cloud
- AlibabaCloud學習代碼:https://github.com/yanghaiji/javayh-cloud-nacos
- SpringBoot+Mybatis 多數據源切換:https://github.com/yanghaiji/javayh-boot-data-soure
- Redis、Mongo、Rabbitmq、Kafka學習代碼: https://github.com/yanghaiji/javayh-middleware
- SpringBoot+SpringSecurity實現自定義登錄學習代碼:https://github.com/yanghaiji/javayh-distribution