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

引言

在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
在这里插入图片描述
5.验证accessToken是否有效:http://localhost:8080/oauth/check_token?token=b212eaec-63a7-489d-b5a2-883ec248c417
在这里插入图片描述
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();
	}
}

在这里插入图片描述

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

在这里插入图片描述

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();// 允许表单登录

	}

}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值