vue+springmvc前后端分离开发(十四)(用oauth2来管理用户令牌)

前情提要

  • 在上一节中,我们实现了用户的登录、注册、注销功能,并且解决了跨域问题。但是仍然有很多问题,比如
    • 在我们成功登录之后,每一次访问需要authenticated()的接口时,都需要携带username和原来的password,这时会出现传输不安全的问题,username和password容易被拦截获取
    • 即使没有上述问题,因为用的是Basic Auth认证模式,所以http请求头部的auth是不支持中文加密的,因此无法正常使用一个中文的用户名
    • 后端并没有数据库匹配用户的登录状态,也不存在session,因此无法有效地判定用户令牌是否过期
    • 不支持第三方登录,一个网站只能以一种方式登录,不方便
  • 基于这些原因,我们需要一个功能更加强大的授权模式,这里选择了oauth2

什么是oauth2?

  • 简单地来说就是一种认证标准,基于这种标准,可以实现通常所说的“第三方登录”,允许访问第三方服务器上的数据

oauth2的运作流程

  • 客户端首先发送授权请求给用户,用户同意授权返回响应给客户端
  • 客户端再发送授权请求给授权服务器,授权服务器同意授权返回token给客户端
  • 客户端携带token发送获取资源的请求给资源服务器,资源服务器理解token并且返回受保护的资源给客户端
  • 以下图出自简书
    oauth2认证流程

在项目中添加oauth2

  • spring项目是集成了oauth2的,我们不需要自己实现授权逻辑与认证协议
  • 在项目中添加oauth2依赖,此依赖比较特殊,不能在ui界面中添加
<dependency>
	<groupId>org.springframework.security.oauth</groupId>
	<artifactId>spring-security-oauth2</artifactId>
	<version>2.2.0.RELEASE</version>
</dependency>

构建符合oauth2规范的数据表

  • 根据文档指出,oauth2至少需要有一张表来存储client,一张表存储用户token,一张表存储refresh_token
  • 官方给出的表结构如下
/* oauth部分 */
/* 客户端表,使用认证前需要向客户端表注册一个client */
CREATE TABLE oauth_client_details (
  client_id varchar(48) NOT NULL,
  resource_ids varchar(256) DEFAULT NULL,
  client_secret varchar(256) DEFAULT NULL,
  `scope` varchar(256) DEFAULT NULL,
  authorized_grant_types varchar(256) DEFAULT NULL,
  web_server_redirect_uri varchar(256) DEFAULT NULL,
  authorities varchar(256) DEFAULT NULL,
  access_token_validity int DEFAULT NULL,
  refresh_token_validity int DEFAULT NULL,
  additional_information text DEFAULT NULL,
  autoapprove varchar(256) DEFAULT NULL,
  PRIMARY KEY (client_id)
);

/* access token */
CREATE TABLE oauth_access_token (
   token_id varchar(256) DEFAULT NULL,
   token blob,
   authentication_id varchar(128) NOT NULL,
   `user_name` varchar(256) DEFAULT NULL,
   client_id varchar(256) DEFAULT NULL,
   authentication blob,
   refresh_token varchar(256) DEFAULT NULL,
   PRIMARY KEY (authentication_id)
);

/* 授权码 */
CREATE TABLE oauth_code (
   code varchar(256) DEFAULT NULL,
   authentication blob,
   create_ts timestamp NULL DEFAULT CURRENT_TIMESTAMP
);

/* approval */
CREATE TABLE oauth_approvals (
   userId varchar(256) DEFAULT NULL,
   clientId varchar(256) DEFAULT NULL,
   `scope` varchar(256) DEFAULT NULL,
   status varchar(10) DEFAULT NULL,
   expiresAt datetime DEFAULT NULL,
   lastModifiedAt datetime DEFAULT NULL
);

/* refresh token */
CREATE TABLE oauth_refresh_token (
  create_time timestamp DEFAULT CURRENT_TIMESTAMP,
  token_id varchar(256),
  token blob,
  authentication blob
);

CREATE INDEX token_id_index ON oauth_refresh_token (token_id);

配置oauth2的授权服务器

  • 根据oauth2的认证流程就可以得知,用户就是我们自己,那么下一步就是配置一个授权服务器了,oauth2依赖中集成了授权服务器,在kmhc.config包中创建AuthorizationServerConfiguration类,代码如下
package kmhc.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;

import kmhc.security.CustomUserDetailsService;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

	@Autowired
	private AuthenticationManager authenticationManager;
	
	@Autowired
	private DataSource dataSource;
	
	@Autowired
	private CustomUserDetailsService userDetailsService;
	
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		// use jdbc service
		clients.withClientDetails(new JdbcClientDetailsService(dataSource));
	}
	
	@Bean
	@Primary
	public AuthorizationServerTokenServices authorizationTokenService() {
		// token service
		DefaultTokenServices tokenServices = new DefaultTokenServices();
		// jdbc client service
		ClientDetailsService clientService = new JdbcClientDetailsService(dataSource);
		tokenServices.setClientDetailsService(clientService);
		tokenServices.setSupportRefreshToken(true);
		// jdbc token store
		TokenStore tokenStore = new JdbcTokenStore(dataSource);
		tokenServices.setTokenStore(tokenStore);
		tokenServices.setSupportRefreshToken(true);
		tokenServices.setAccessTokenValiditySeconds(7200);
		tokenServices.setRefreshTokenValiditySeconds(259200);
		return tokenServices;
	}
	
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		// User Details
		endpoints.userDetailsService(userDetailsService);
		// token management service
		endpoints.tokenServices(authorizationTokenService());
		// password mode need
		endpoints.authenticationManager(authenticationManager);
		// code mode need
		endpoints.authorizationCodeServices(new JdbcAuthorizationCodeServices(dataSource));
		// allow post
		endpoints.allowedTokenEndpointRequestMethods(HttpMethod.POST);
		// approval
		ApprovalStore approvalStore = new JdbcApprovalStore(dataSource);
		endpoints.approvalStore(approvalStore);
	}
	
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) {
		security
			.tokenKeyAccess("permitAll()")
			.checkTokenAccess("permitAll()")
			.allowFormAuthenticationForClients();
	}
}
  • 上述代码难以理解的地方还是有很多的,希望大家可以通过查阅资料自行理解
  • 注意到在@Autowired中注入了AuthenticationManager,虽然是springsecurity提供的,但是如果不显示地将它声明为@Bean并且返回是会报错的,所以需要在SecurityConfig类中加入一个@Bean
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
	return super.authenticationManagerBean();
}

配置oauth2的资源服务器

  • 在多服务器的情况下,资源服务器和授权服务器一般是分开的,但是我们只有自己的电脑,所以资源服务器也得配置为自己
  • 同样是kmhc.config包,创建一个ResourceServerConfiguration类,代码如下
package kmhc.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

	// the name of resource id
	public static final String RESOURCE_ID = "kmhc";
	
	@Bean
	public ResourceServerTokenServices resourceTokenService() {
		RemoteTokenServices service = new RemoteTokenServices();
		service.setCheckTokenEndpointUrl("http://127.0.0.1:9001/oauth/check_token");
		service.setClientId("kmhc");
		service.setClientSecret("123456");
		return service;
	}
	
	@Override
	public void configure(ResourceServerSecurityConfigurer resources) {
		resources.resourceId(RESOURCE_ID)
			.tokenServices(resourceTokenService())
			.stateless(true);
	}
	
	@Override
	public void configure(HttpSecurity http) throws Exception {
		http
			.csrf().disable()
			.cors()
			.and()
			.authorizeRequests()
			.antMatchers(HttpMethod.POST, "/api/login").permitAll()
			.antMatchers(HttpMethod.POST, "/api/users").permitAll()
			.antMatchers(HttpMethod.GET, "/api/users/{username}").permitAll()
			.antMatchers("/api/users/{username}").authenticated()
			.antMatchers(HttpMethod.GET, "/api/users").hasRole("ADMIN")	
			.antMatchers("/api/authorities/**").hasRole("ADMIN")
			.antMatchers("/api/groups/**").hasRole("ADMIN")
			.antMatchers("/api/groupAuthorities/**").hasRole("ADMIN")
			.and()
			.formLogin().disable()
			.httpBasic()
			.and()
			.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
	}
}
  • 在资源服务器的配置中,同样也有http的配置,这里的http配置会覆盖掉spring security的http配置

注册一个client

  • 上文也说过了,想要使用oauth2的认证,必须先获得许可,即在oauth_client_details表中添加一条记录,输入如下命令添加一条记录
INSERT INTO oauth_client_details VALUES("kmhc", "kmhc", "$2a$10$wDk9AuORolw2M1486MReXuwMZbJRCrtvzH/EHopvk/8EufqaAts86", "all", "authorization_code,password,refresh_token", "http://127.0.0.1:9001/", "ROLE_USER", 7200, 86400, NULL, "TRUE");

用户名密码模式

  • 现在已经配置好了oauth2的所有配置,可以用postman开始测试了,输入数据和输出数据如下
    oauth返回令牌
  • 返回了这么一个json数据
{
    "access_token": "2574b19e-36b6-411c-8446-7e16a9d2ed05",
    "token_type": "bearer",
    "refresh_token": "07ed80e9-b810-4a07-9dd2-9b3bafcdc47b",
    "expires_in": 7199,
    "scope": "all"
}
  • 对json数据解析
    • access_token是用户令牌,以后用户都需要在headers的Authorization中添加令牌来访问接口
    • token_type是令牌类型,此处为bearer,在postman的Authorization中可以找到它,然后将access_token添加进去即可
    • refresh_token用于刷新令牌的生效时间,access_token设置的有效期为2小时,refresh_token设置的有效期为24小时
    • scope表示该令牌所能访问的应用范围,all表示整个网站都能访问
  • 再看一下数据库的表,是否真的有数据,输入命令select count(*) from oauth_access_token,发现有1条数据在里面,说明授权成功

至此,oauth2的后端配置部分已经讲解完毕,下一节会讲解如何在前端使用oauth2来进行认证,进行令牌的交换

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringCloud是一款开源的微服务框架,OAuth2是一种授权框架,Vue是一个流行的前端框架,ElementUI是一套基于Vue开发的UI框架。结合这些技术栈进行前后端分离的快速上手项目实战开发,可以提高开发效率和代码的可维护性。 实践中,可以按照以下步骤进行快速上手项目开发: 1. 搭建后端服务:使用SpringCloud搭建微服务架构,并引入Spring Security和OAuth2来实现认证和授权功能,确保后端接口的安全性。 2. 配置OAuth2服务端:在后端服务中配置OAuth2的服务端,定义认证服务器和资源服务器,配置客户端信息,如客户端ID、客户端密钥等。 3. 开发前端界面:使用Vue构建前端界面,并引入ElementUI来快速搭建页面和组件。利用Vue的组件化开发方式,可以更高效地开发各种交互功能。 4. 实现登录认证:在前端界面中使用OAuth2的授权码模式来实现用户登录认证功能,通过向认证服务器发送请求来获取访问令牌,并将令牌保存到前端的Cookie或localStorage中。 5. 发起请求并解析响应:在前端界面中使用Axios库来发起HTTP请求,并在请求头中携带访问令牌,后端服务器根据令牌进行权限验证。前端收到响应后解析数据,并进行相应的操作。 6. 实现权限控制:根据后端接口的权限设定,在前端界面中进行权限控制,隐藏或禁用没有权限的功能。可以通过在请求头中携带用户的角色信息,与后端进行验证。 7. 编写测试用例:保证代码的质量和功能的稳定性,编写相应的测试用例来进行单元测试和接口测试,确保项目的正确运行。 通过以上步骤,可以快速上手并实战开发SpringCloud、OAuth2、Vue和ElementUI结合的前后端分离项目。不仅可以提高开发效率,还能保证项目的安全性和可维护性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值