SpringCloud+OAuth2實現統一權限驗證,並持久化到Mysql中

社區羣聊

添加小編微信進入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();
	}

}

配置文件

這裏只寫了一部分,詳情參考https://github.com/Dylan-haiji/javayh-platform/blob/master/javayh-sso/javayh-resource/src/main/resources/bootstrap.yml

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

這時有兩種方式訪問

本問全部源碼以放在github上,歡迎點贊,或者加入開發,也可加入社區交流羣,添加小編微信:372787553
地址:https://github.com/Dylan-haiji/javayh-platform/tree/master/javayh-sso

本文的分享暫時就到這裏,希望對您有所幫助
關注 Java有貨領取更多資料

聯繫小編。微信:372787553,帶您進羣互相學習
左側小編微信,右側獲取免費資料
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章