欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

微服务技术系列教程(41)- SpringCloud -OAuth2搭建微服务开放平台

程序员文章站 2023-12-24 08:11:15
...

引言

在Spring Cloud需要使用oauth2来实现多个微服务的统一认证授权,通过向OAuth服务发送某个类型的grant type进行集中认证和授权,从而获得access_token,而这个token是受其他微服务信任的,我们在后续的访问可以通过access_token来进行,从而实现了微服务的统一认证授权。

客户端根据约定的ClientID、ClientSecret、Scope来从Access Token URL地址获取AccessToken,并经过AuthURL认证,用得到的AccessToken来访问其他资源接口。

Spring Cloud OAuth2 需要依赖Spring Security。

OAuth2角色划分:

  • 「Resource Server」:被授权访问的资源
  • 「Authotization Server」:OAuth2认证授权中心
  • 「Resource Owner」: 用户
  • 「Client」:使用API的客户端(如Android 、IOS、web app)

OAuth2四种授权方式:

  • 授权码模式(authorization code):用在客户端与服务端应用之间授权
  • 简化模式(implicit):用在移动app或者web app(这些app是在用户的设备上的,如在手机上调起微信来进行认证授权)
  • 密码模式(resource owner password credentials):应用直接都是受信任的(都是由一家公司开发的)
  • 客户端模式(client credentials):用在应用API访问

2. OAuth2 环境搭建

2.1 认证授权中心服务

2.1.1 密码模式

1.添加maven依赖:

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.0.1.RELEASE</version>
</parent>
<!-- 管理依赖 -->
<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>Finchley.M7</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>
<dependencies>
	<!-- SpringBoot整合Web组件 -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
	</dependency>

	<!-- springboot整合freemarker -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-freemarker</artifactId>
	</dependency>

	<!-->spring-boot 整合security -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-security</artifactId>
	</dependency>
	<!-- spring-cloud-starter-oauth2 -->
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-oauth2</artifactId>
	</dependency>


</dependencies>
<!-- 注意: 这里必须要添加, 否者各种依赖有问题 -->
<repositories>
	<repository>
		<id>spring-milestones</id>
		<name>Spring Milestones</name>
		<url>https://repo.spring.io/libs-milestone</url>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
	</repository>
</repositories>

2.创建授权配置信息

// 配置授权中心信息
@Configuration
@EnableAuthorizationServer // 开启认证授权中心
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	// accessToken有效期
	private int accessTokenValiditySeconds = 7200; // 两小时

	// 添加商户信息
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		// withClient appid
		clients.inMemory().withClient("client_1").secret(passwordEncoder().encode("123456"))
				.authorizedGrantTypes("password","client_credentials","refresh_token").scopes("all").accessTokenValiditySeconds(accessTokenValiditySeconds);
	}

	// 设置token类型
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
		endpoints.authenticationManager(authenticationManager()).allowedTokenEndpointRequestMethods(HttpMethod.GET,
				HttpMethod.POST);
	}

	@Override
	public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
		// 允许表单认证
		oauthServer.allowFormAuthenticationForClients();
		// 允许check_token访问
		oauthServer.checkTokenAccess("permitAll()");
	}

	@Bean
	AuthenticationManager authenticationManager() {
		AuthenticationManager authenticationManager = new AuthenticationManager() {

			public Authentication authenticate(Authentication authentication) throws AuthenticationException {
				return daoAuhthenticationProvider().authenticate(authentication);
			}
		};
		return authenticationManager;
	}

	@Bean
	public AuthenticationProvider daoAuhthenticationProvider() {
		DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
		daoAuthenticationProvider.setUserDetailsService(userDetailsService());
		daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
		daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
		return daoAuthenticationProvider;
	}

	// 设置添加用户信息,正常应该从数据库中读取
	@Bean
	UserDetailsService userDetailsService() {
		InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
		userDetailsService.createUser(User.withUsername("user_1").password(passwordEncoder().encode("123456"))
				.authorities("ROLE_USER").build());
		userDetailsService.createUser(User.withUsername("user_2").password(passwordEncoder().encode("1234567"))
				.authorities("ROLE_USER").build());
		return userDetailsService;
	}

	@Bean
	PasswordEncoder passwordEncoder() {
		// 加密方式
		PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
		return passwordEncoder;
	}
}

3.启动授权服务

@SpringBootApplication
public class AppOauth2 {
	public static void main(String[] args) {
		SpringApplication.run(AppOauth2.class, args);
	}

}

4.获取accessToken请求地址: http://localhost:8080/oauth/token
微服务技术系列教程(41)- SpringCloud -OAuth2搭建微服务开放平台
5.验证accessToken是否有效:http://localhost:8080/oauth/check_token?token=b212eaec-63a7-489d-b5a2-883ec248c417
微服务技术系列教程(41)- SpringCloud -OAuth2搭建微服务开放平台
6.刷新新的accessToken:http://localhost:8080/oauth/token?grant_type=refresh_token&refresh_token=4803dbfe-41c8-417c-834e-6be6b296b767&client_id=client_1&client_secret=123456,需要配置:

public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
		endpoints.authenticationManager(authenticationManager()).allowedTokenEndpointRequestMethods(HttpMethod.GET,
				HttpMethod.POST);

		endpoints.authenticationManager(authenticationManager());
		endpoints.userDetailsService(userDetailsService());
	}

2.1.2 授权模式

1.新增授权权限

	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		// withClient appid
		clients.inMemory().withClient("client_1").secret(passwordEncoder().encode("123456"))
				.authorizedGrantTypes("password", "client_credentials", "refresh_token", "authorization_code")
				.scopes("all").redirectUris("http://www.xxx.com")
				.accessTokenValiditySeconds(accessTokenValiditySeconds)
				.refreshTokenValiditySeconds(refreshTokenValiditySeconds);
	}

2.请求http://localhost:8080/oauth/authorize?response_type=code&client_id=client_1&redirect_uri=http://www.xxx.com ,访问报错:
User must be authenticated with Spring Security before authorization can be completed.

3.解决办法 添加Security权限

@Component
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	// 授权中心管理器
	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		AuthenticationManager manager = super.authenticationManagerBean();
		return manager;
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	// 拦截所有请求,使用httpBasic方式登陆
	@Override
	protected void configure(HttpSecurity http) throws Exception {

		http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().httpBasic();
	}
}

微服务技术系列教程(41)- SpringCloud -OAuth2搭建微服务开放平台

2.2 资源服务端

一个资源服务器,各个服务之间的通信(访问需要权限的资源)时需携带访问令牌

资源服务器通过 @EnableResourceServer 注解来开启一个 OAuth2AuthenticationProcessingFilter 类型的过滤器,通过继承 ResourceServerConfigurerAdapter 类来配置资源服务器。

1.添加maven依赖

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.0.1.RELEASE</version>
</parent>
<!-- 管理依赖 -->
<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>Finchley.M7</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>
<dependencies>
	<!-- SpringBoot整合Web组件 -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
	</dependency>

	<!-- springboot整合freemarker -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-freemarker</artifactId>
	</dependency>

	<!-->spring-boot 整合security -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-security</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-oauth2</artifactId>
	</dependency>


</dependencies>
<!-- 注意: 这里必须要添加, 否者各种依赖有问题 -->
<repositories>
	<repository>
		<id>spring-milestones</id>
		<name>Spring Milestones</name>
		<url>https://repo.spring.io/libs-milestone</url>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
	</repository>
</repositories>

2.application.yml

server:
  port: 8081


logging:
  level:
    org.springframework.security: DEBUG

security:
  oauth2:
    resource:
      ####从认证授权中心上验证token
      tokenInfoUri: http://localhost:8080/oauth/check_token
      preferTokenInfo: true
    client:
      accessTokenUri: http://localhost:8080/oauth/token
      userAuthorizationUri: http://localhost:8080/oauth/authorize
      ###appid
      clientId: client_1
      ###appSecret
      clientSecret: 123456

3.资源拦截配置

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

	@Override
	public void configure(HttpSecurity http) throws Exception {
		// 对 api/order 请求进行拦截
		http.authorizeRequests().antMatchers("/api/order/**").authenticated();
	}

}

4.资源服务请求

@RestController
@RequestMapping("/api/order")
public class OrderController {

	@RequestMapping("/addOrder")
	public String addOrder() {
		return "addOrder";
	}

}

5.启动权限

@SpringBootApplication
@EnableOAuth2Sso
public class AppOrder {

	public static void main(String[] args) {
		SpringApplication.run(AppOrder.class, args);
	}
}

6.资源访问,请求资源: http://127.0.0.1:8081/api/order/addOrder
Authorization: bearer 31820c84-2e52-408f-9d21-a62483aad59d

微服务技术系列教程(41)- SpringCloud -OAuth2搭建微服务开放平台

3. 将应用信息改为数据库存储

官方推荐SQL:https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql

1.添加maven依赖:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
</dependency>

2.application.yml

spring:
  datasource:
    hikari:
      connection-test-query: SELECT 1
      minimum-idle: 1
      maximum-pool-size: 5
      pool-name: dbcp1
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/alan-oauth?autoReconnect=true&useSSL=false
    username: root
    password: 123456

3.修改配置文件类

// 配置授权中心信息
@Configuration
@EnableAuthorizationServer // 开启认证授权中心
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

	@Autowired
	@Qualifier("authenticationManagerBean")
	private AuthenticationManager authenticationManager;

	@Autowired
	@Qualifier("dataSource")
	private DataSource dataSource;

	// @Autowired
	// private UserDetailsService userDetailsService;

	@Bean
	public TokenStore tokenStore() {
		// return new InMemoryTokenStore(); //使用内存中的 token store
		return new JdbcTokenStore(dataSource); /// 使用Jdbctoken store
	}

	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

		// 添加授权用户
		clients.jdbc(dataSource);
//		.withClient("client_1").secret(new BCryptPasswordEncoder().encode("123456"))
//		.authorizedGrantTypes("password", "refresh_token", "authorization_code")// 允许授权范围
//		.redirectUris("http://www.xxx.com").authorities("ROLE_ADMIN", "ROLE_USER")// 客户端可以使用的权限
//		.scopes("all").accessTokenValiditySeconds(7200).refreshTokenValiditySeconds(7200);
	}

	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager)
				.userDetailsService(userDetailsService());// 必须设置
															// UserDetailsService
															// 否则刷新token 时会报错
	}

	@Bean
	UserDetailsService userDetailsService() {
		InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
		userDetailsService.createUser(User.withUsername("user_1").password(new BCryptPasswordEncoder().encode("123456"))
				.authorities("ROLE_USER").build());
		userDetailsService.createUser(User.withUsername("user_2")
				.password(new BCryptPasswordEncoder().encode("1234567")).authorities("ROLE_USER").build());
		return userDetailsService;
	}

	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()")
				.allowFormAuthenticationForClients();// 允许表单登录

	}

}

上一篇:

下一篇: