目录

 (一) 简介

OAuth是一个关于授权的开放网络标准,在全世界得到的广泛的应用,目前是2.0的版本。OAuth2在“客户端”与“服务提供商”之间,设置了一个授权层(authorization layer)。“客户端”不能直接登录“服务提供商”,只能登录授权层,以此将用户与客户端分离。“客户端”登录需要OAuth提供的令牌,否则将提示认证失败而导致客户端无法访问服务。OAuth2.0是OAuth协议的延续版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0。

OAuth2为我们提供了四种授权方式:

1、授权码模式(authorization code)
2、简化模式(implicit)
3、密码模式(resource owner password credentials)
4、客户端模式(client credentials)

授权码模式

授权码相对其他三种来说是功能比较完整、流程最安全严谨的授权方式,通过客户端的后台服务器与服务提供商的认证服务器交互来完成。流程如下图所示:

简化模式

这种模式不通过服务器端程序来完成,直接由浏览器发送请求获取令牌,令牌是完全暴露在浏览器中的,这种模式极力不推崇。流程如下图所示:

密码模式

密码模式也是比较常用到的一种,客户端向授权服务器提供用户名、密码然后得到授权令牌。这种模式不过有种弊端,我们的客户端需要存储用户输入的密码,但是对于用户来说信任度不高的平台是不可能让他们输入密码的。流程如下图所示:

客户端模式

客户端模式是客户端以自己的名义去授权服务器申请授权令牌,并不是完全意义上的授权。如下图所示:

上述简单的介绍了OAuth2内部的四种授权方式,我们下面使用密码模式来进行测试;我们就来讲解下SpringBoot项目中是如何配置使用OAuth2服务器端,并且我们使用数据库中的用户数据来做验证处理,并让OAuth2整合SpringSecurity来保护我们的REST接口。

(二) 建表,初始化数据

Oauth2相关的5张表:

  • oauth_access_token:访问令牌
  • oauth_refresh_token:更新令牌
  • oauth_client_details:客户端信息
  • oauth_code:授权码
  • oauth_approvals:授权记录
  • oauth_client_token:  客户端用来记录token信息

只以密码模式来进行测试,不考虑管理功能,只用到了了oauth_client_details,oauth_access_token,oauth_refresh_token 三张表

DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token` (
`token_id` varchar(255) DEFAULT NULL COMMENT '加密的access_token的值',
`token` longblob COMMENT 'OAuth2AccessToken.java对象序列化后的二进制数据',
`authentication_id` varchar(255) DEFAULT NULL COMMENT '加密过的username,client_id,scope',
`user_name` varchar(255) DEFAULT NULL COMMENT '登录的用户名',
`client_id` varchar(255) DEFAULT NULL COMMENT '客户端ID',
`authentication` longblob COMMENT 'OAuth2Authentication.java对象序列化后的二进制数据',
`refresh_token` varchar(255) DEFAULT NULL COMMENT '加密的refresh_token的值'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  1.  

DROP TABLE IF EXISTS `oauth_approvals`;
CREATE TABLE `oauth_approvals` (
`userId` varchar(255) DEFAULT NULL COMMENT '登录的用户名',
`clientId` varchar(255) DEFAULT NULL COMMENT '客户端ID',
`scope` varchar(255) DEFAULT NULL COMMENT '申请的权限范围',
`status` varchar(10) DEFAULT NULL COMMENT '状态(Approve或Deny)',
`expiresAt` datetime DEFAULT NULL COMMENT '过期时间',
`lastModifiedAt` datetime DEFAULT NULL COMMENT '最终修改时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  1.  

DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
`client_id` varchar(255) NOT NULL COMMENT '客户端ID',
`resource_ids` varchar(255) DEFAULT NULL COMMENT '资源ID集合,多个资源时用逗号(,)分隔',
`client_secret` varchar(255) DEFAULT NULL COMMENT '客户端密匙',
`scope` varchar(255) DEFAULT NULL COMMENT '客户端申请的权限范围',
`authorized_grant_types` varchar(255) DEFAULT NULL COMMENT '客户端支持的grant_type',
`web_server_redirect_uri` varchar(255) DEFAULT NULL COMMENT '重定向URI',
`authorities` varchar(255) DEFAULT NULL COMMENT '客户端所拥有的Spring Security的权限值,多个用逗号(,)分隔',
`access_token_validity` int(11) DEFAULT NULL COMMENT '访问令牌有效时间值(单位:秒)',
`refresh_token_validity` int(11) DEFAULT NULL COMMENT '更新令牌有效时间值(单位:秒)',
`additional_information` varchar(255) DEFAULT NULL COMMENT '预留字段',
`autoapprove` varchar(255) DEFAULT NULL COMMENT '用户是否自动Approval操作'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  1.  

DROP TABLE IF EXISTS `oauth_client_token`;
CREATE TABLE `oauth_client_token` (
`token_id` varchar(255) DEFAULT NULL COMMENT '加密的access_token值',
`token` longblob COMMENT 'OAuth2AccessToken.java对象序列化后的二进制数据',
`authentication_id` varchar(255) DEFAULT NULL COMMENT '加密过的username,client_id,scope',
`user_name` varchar(255) DEFAULT NULL COMMENT '登录的用户名',
`client_id` varchar(255) DEFAULT NULL COMMENT '客户端ID'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  1.  

DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code` (
`code` varchar(255) DEFAULT NULL COMMENT '授权码(未加密)',
`authentication` varbinary(255) DEFAULT NULL COMMENT 'AuthorizationRequestHolder.java对象序列化后的二进制数据'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  1.  

DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (
`token_id` varchar(255) DEFAULT NULL COMMENT '加密过的refresh_token的值',
`token` longblob COMMENT 'OAuth2RefreshToken.java对象序列化后的二进制数据 ',
`authentication` longblob COMMENT 'OAuth2Authentication.java对象序列化后的二进制数据'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  1.  

DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL COMMENT '用户名',
`password` varchar(50) DEFAULT NULL COMMENT '密码',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息表';

凭证(账号)和权限表3张

  • authority 权限表
  • credentials  凭证表(相当于用户账号表)
  • credentials_authorities 授权表(以上2个表的关联表)

现实项目中对应的是用户,角色,权限表

  1.  

DROP TABLE IF EXISTS `authority`;
CREATE TABLE `authority` (
`id` bigint(11) NOT NULL COMMENT '权限id',
`authority` varchar(255) DEFAULT NULL COMMENT '权限',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  1.  

DROP TABLE IF EXISTS `credentials`;
CREATE TABLE `credentials` (
`id` bigint(11) NOT NULL COMMENT '凭证id',
`enabled` tinyint(1) NOT NULL COMMENT '是否可用',
`name` varchar(255) NOT NULL COMMENT '用户名',
`password` varchar(255) NOT NULL COMMENT '密码',
`version` int(11) DEFAULT NULL COMMENT '版本号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  1.  

DROP TABLE IF EXISTS `credentials_authorities`;
CREATE TABLE `credentials_authorities` (
`credentials_id` bigint(20) NOT NULL COMMENT '凭证id',
`authorities_id` bigint(20) NOT NULL COMMENT '权限id'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

初始化数据

  • 定义了3个角色3个账号
  • 以“project_api” 命名resourceServer中的api请求路径,我们定义一个客户端叫做:user-client(认证权限类型:read,write)
  • 密码都是user,数据库中存的是加密过后的字符串
  1. INSERT INTO authority VALUES(1,'ROLE_OAUTH_ADMIN');
  2. INSERT INTO authority VALUES(2,'ROLE_RESOURCE_ADMIN');
  3. INSERT INTO authority VALUES(3,'ROLE_PROJECT_ADMIN');
  4. INSERT INTO credentials VALUES(1,b'','oauth_admin','$2a$10$BurTWIy5NTF9GJJH4magz.9Bd4bBurWYG8tmXxeQh1vs7r/wnCFG2','');
  5. INSERT INTO credentials VALUES(2,b'','resource_admin','$2a$10$BurTWIy5NTF9GJJH4magz.9Bd4bBurWYG8tmXxeQh1vs7r/wnCFG2','');
  6. INSERT INTO credentials VALUES(3,b'','project_admin','$2a$10$BurTWIy5NTF9GJJH4magz.9Bd4bBurWYG8tmXxeQh1vs7r/wnCFG2','');
  7. INSERT INTO credentials_authorities VALUE (1,1);
  8. INSERT INTO credentials_authorities VALUE (2,2);
  9. INSERT INTO credentials_authorities VALUE (3,3);
  10.  
  11. INSERT INTO oauth_client_details VALUES('user_client','project_api', '$2a$10$BurTWIy5NTF9GJJH4magz.9Bd4bBurWYG8tmXxeQh1vs7r/wnCFG2', 'read,write', 'password,refresh_token', 'http://127.0.0.1', 'ROLE_PROJECT_ADMIN', 7200, 1800, NULL, 'true');

(三)工程配置

创建父工程oauth2,继续建立两个子模块:

  • Authorization Server - 授权服务器
  • Resource Server - 资源服务器

具体配置下载工程源码查看

(四)Authorization Server - Spring Security配置

创建一个spring security 配置类,在配置类中注入了上面我们自定义的自定义UserDetailsService以及用户密码验证器。

  1.  
  1. package com.oauth2.authorization.config;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    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.HttpSecurity;
    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;
    import org.springframework.security.crypto.password.PasswordEncoder;
  4.  
  5. /**
    * spring security 配置类
    */
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true) //开启security注解
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  6.  
  7. /**
    * 密码编码验证器
    * @return
    */
    @Bean
    public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
    }
  8.  
  9. /**
    * 自定义UserDetailsService用来从数据库中根据用户名查询用户信息以及角色信息
    */
    @Autowired
    public UserDetailsService userDetailsService;
  10.  
  11. @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
    }
  12.  
  13. @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService)
    .passwordEncoder(passwordEncoder());
    }
  14.  
  15. /**
    * 验证配置
    * @param http
    * @throws Exception
    */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    http
    .authorizeRequests()
    .antMatchers("/**").authenticated()
    .and()
    .userDetailsService(userDetailsService);
    }
    } 

 自定义UserDetailsService

创建一个名叫JdbcUserDetails的类实现UserDetailsService接口,代码如下:

  1. public class JdbcUserDetails implements UserDetailsService {
  2.  
  3. @Autowired
  4. private CredentialsDao credentialsDao;
  5.  
  6. @Override
  7. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  8.  
  9. Credentials credentials = credentialsDao.findByName(username);
  10. if (credentials == null) {
  11. throw new UsernameNotFoundException("User '" + username + "' can not be found");
  12. }
  13.  
  14. return new User(credentials.getName(), credentials.getPassword(), credentials.isEnabled(), true, true, true, credentials.getGrantedAuthorities());
  15. }
  16.  
  17. }

(五)Authorization Server - 授权服务器

授权服务器负责验证用户标识并提供令牌,使用@EnableAuthorizationServer注解启用授权服务器配置。

  1. package com.oauth2.authorization.config;
  2.  
  3. import com.oauth2.authorization.userdetails.JdbcUserDetails;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.password.PasswordEncoder;
    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.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.AuthorizationCodeServices;
    import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
  4.  
  5. import javax.sql.DataSource;
  6.  
  7. /**
    * 授权服务器配置
    */
    @Configuration
    @EnableAuthorizationServer //注解开启了验证服务器
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
  8.  
  9. @Autowired
    private DataSource dataSource;
  10.  
  11. @Autowired
    private PasswordEncoder passwordEncoder;
  12.  
  13. @Autowired
    private AuthenticationManager authenticationManager;
  14.  
  15. @Bean
    public JdbcClientDetailsService jdbcClientDetailsService() {
    return new JdbcClientDetailsService(dataSource);
    }
  16.  
  17. @Bean
    public TokenStore tokenStore() {
    return new JdbcTokenStore(dataSource);
    }
  18.  
  19. @Autowired
    public UserDetailsService userDetailsService;
  20.  
  21. /**
    * 配置 token 节点的安全策略
    * @param security
    * @throws Exception
    */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    security.tokenKeyAccess("permitAll()"); // 获取 token 的策略
    security.checkTokenAccess("isAuthenticated()");
    }
  22.  
  23. /**
    * 配置客户端信息
    *
    * @param clients
    * @throws Exception
    */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.withClientDetails(jdbcClientDetailsService()); //设置客户端的配置从数据库中读取,存储在oauth_client_details表
    }
  24.  
  25. @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints.authenticationManager(authenticationManager) // 开启密码验证,来源于 WebSecurityConfigurerAdapter
    .userDetailsService(userDetailsService) // 读取验证用户的信息
    .tokenStore(tokenStore());
  26.  
  27. }
  28.  
  29. } 

(六)Resource Server - 资源服务器

资源服务器,受OAuth2令牌保护的资源

  1. package com.oauth2.resources.config;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    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.configuration.EnableWebSecurity;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    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.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
    import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
  4.  
  5. import javax.sql.DataSource;
  6.  
  7. /**
    * 资源服务器配置
    */
  8.  
  9. @Configuration
    @EnableResourceServer
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class ResourcesServerConfig extends ResourceServerConfigurerAdapter {
  10.  
  11. @Autowired
    private DataSource dataSource;
  12.  
  13. @Bean
    public BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
    }
  14.  
  15. @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;
  16.  
  17. @Bean
    public TokenStore tokenStore() {
    return new JdbcTokenStore(dataSource);
    }
  18.  
  19. @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    resources.resourceId("project_api").stateless(false);
    resources.tokenStore(tokenStore());
    }
  20.  
  21. @Override
    public void configure(HttpSecurity http) throws Exception {
    http
    .logout()
    .logoutUrl("/logout")//虚拟的登出地址
    .logoutSuccessHandler(logoutSuccessHandler)//登出做的操作
    .and()
    .authorizeRequests()
    .antMatchers("/test/hello").permitAll()
    .antMatchers("/test/**").authenticated();
    }
    }

我们这里设置了一个LogoutSuccessHandler,他的作用是请求/logout地址时,清空数据库中的accessToken,防止被窃取用于访问,代码如下:

  1. package com.oauth2.resources.config;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.security.core.Authentication;
  5. import org.springframework.security.oauth2.common.OAuth2AccessToken;
  6. import org.springframework.security.oauth2.provider.token.TokenStore;
  7. import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
  8. import org.springframework.stereotype.Component;
  9.  
  10. import javax.servlet.ServletException;
  11. import javax.servlet.http.HttpServletRequest;
  12. import javax.servlet.http.HttpServletResponse;
  13. import java.io.IOException;
  14.  
  15. /**
  16. * 登出清空accessToken
  17. */
  18.  
  19. @Component
  20. public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
  21.  
  22. private static String BEARER_AUTHENTICATION = "Bearer";
  23.  
  24. private static String HEADER_AUTHENTICATION = "authorization";
  25.  
  26. @Autowired
  27. private TokenStore tokenStore;
  28.  
  29. @Override
  30. public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
  31. String auth = httpServletRequest.getHeader(HEADER_AUTHENTICATION);
  32. String token = httpServletRequest.getParameter("access_token");
  33. if (auth != null && auth.startsWith(BEARER_AUTHENTICATION)) {
  34. token = token.split(" ")[0];
  35. }
  36.  
  37. if (token != null) {
  38. OAuth2AccessToken accessToken = tokenStore.readAccessToken(token);
  39. if (accessToken != null) {
  40. tokenStore.removeAccessToken(accessToken);
  41. }
  42. }
  43. }
  44.  
  45. }

(七)测试

资源服务器测试代码

  1. package com.oauth2.resources.controller;
  2.  
  3. import org.springframework.security.access.prepost.PreAuthorize;
  4. import org.springframework.security.core.Authentication;
  5. import org.springframework.security.core.context.SecurityContextHolder;
  6. import org.springframework.web.bind.annotation.GetMapping;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.RestController;
  9.  
  10. @RestController
  11. @RequestMapping(value = "/test")
  12. public class TestController {
  13.  
  14. @GetMapping("/hello")
  15. public String hello(){
  16. return "Hello";
  17. }
  18.  
  19. @GetMapping("/meet")
  20. public String meet(){
  21. return "I meet you";
  22. }
  23.  
  24. @GetMapping("/welcome")
  25. public String welcome(){
  26. Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  27. return "Welcome " + authentication.getName();
  28. }
  29.  
  30. @GetMapping("/project")
  31. @PreAuthorize("hasRole('ROLE_PROJECT_ADMIN')") //具有此角色
  32. public String project(){
  33. return "This is my project";
  34. }
  35.  
  36. }

在ResourceServerConfig配置中,只有/test/hello不需要授权,其他都需要授权。

1. 直接访问 http://localhost:8081/test/hello ,无需授权,所以正常响应,输出hello字符串。

2.  访问 http://localhost:8081/test/meet

可以看到正如我们预期一样,返回了401错误以及错误信息,下面我们来获取access_token。

3. Spring Security OAuth2默认提供的四个URL

  • /oauth/authorize : 授权AuthorizationEndpoint
  • /oauth/token : 令牌TokenEndpoint
  • /oauth/check_token : 令牌校验CheckTokenEndpoint
  • /oauth/confirm_access : 授权页面WhitelabelApprovalEndpoint
  • /oauth/error : 错误页面WhitelabelErrorEndpoint

在获取token之前需要在数据库表oauth_client_details添加对应的数据 ,见上方的初始化数据,初始化了一条客户端配置信息。

4.获得令牌,POST请求 http://localhost:8080/oauth/token?grant_type=password&username=project_admin&password=user

可以看到我们访问的地址,grant_type使用到了password模式,username和password即credentials表中的name和password字段值。

获取access_token需要响应头中添加客户端的授权信息,通过Postman工具的头授权信息即可输出对应的值就可以完成Basic Auth的加密串生成,clientid、secret的值存执表oauth_client_details中对应字段。

成功访问后oauth2给我们返回了几个参数:

  • access_token:本地访问获取到的access_token,会自动写入到数据库中。
  • token_type:获取到的access_token的授权方式
  • refersh_token:刷新token时所用到的授权
  • tokenexpires_in:有效期(从获取开始计时,值秒后过期)
  • scope:客户端的接口操作权限(read:读,write:写)

 3.带上授权服务器返回的access_token发访问 http://localhost:8081/test/meet?access_token=7afa7ff0-2e17-4388-b8c7-47355de57537

成功输出。

5. 访问http://localhost:8081/test/welcome?access_token=7afa7ff0-2e17-4388-b8c7-47355de57537

成功打印出了用户的账号。

6. 访问http://localhost:8081/test/project?access_token=7afa7ff0-2e17-4388-b8c7-47355de57537

成功输出。

从代码上到,project方法使用了PreAuthorize注解,要求用户具有ROLE_PROJECT_ADMIN角色才能访问,如果使用一个不具有ROLE_PROJECT_ADMIN角色的账号的access_token访问,将出现下列403提示access_denied:

7.  访问 http://localhost:8081/logout?access_token=7afa7ff0-2e17-4388-b8c7-47355de57537 ,token将被从数据库中删除

再使用该token将提示invalid_token:

(八)工程下载

https://download.csdn.net/download/zsg88/11603183

Springboot2+SpringSecurity+Oauth2+Mysql数据库实现持久化客户端数据的更多相关文章

  1. 使用Nodejs实现实时推送MySQL数据库最新信息到客户端

    下面我们要做的就是把MySQL这边一张表数据的更新实时的推送到客户端,比如MySQL这边表的数据abc变成123了,那使用程序就会把最新的123推送到每一个连接到服务器的客户端.如果服务器的连接的客户 ...

  2. MySQL数据库如何解决大数据量存储问题

    利用MySQL数据库如何解决大数据量存储问题? 各位高手您们好,我最近接手公司里一个比较棘手的问题,关于如何利用MySQL存储大数据量的问题,主要是数据库中的两张历史数据表,一张模拟量历史数据和一张开 ...

  3. MySQL数据库使用mysqldump导出数据详解

    mysqldump是mysql用于转存储数据库的实用程序.它主要产生一个SQL脚本,其中包含从头重新创建数据库所必需的命令CREATE TABLE INSERT等.接下来通过本文给大家介绍MySQL数 ...

  4. ABP框架使用Mysql数据库,以及基于SQLServer创建Mysql数据库的架构和数据

    ABP默认的数据库是SQLServer,不过ABP框架底层是EF框架,因此也是很容易支持其他类型的数据库的,本篇随笔介绍在ABP框架使用Mysql数据库,以及基于SQLServer创建MySql数据库 ...

  5. C#实现MySQL数据库中的blob数据存储

    在MySQL数据库中,有一种blob数据类型,用来存储文件.C#编程语言操作MySQL数据库需要使用MySQL官方组件MySQL.Data.dll. Mysql.Data.dll(6.9.6)组件下载 ...

  6. ubuntu 下 mysql数据库的搭建 及 数据迁移

    1.mysql的安装 我是使用apt-get直接安装的 :sudo apt-get install mysql-server sudo apt-get install mysql-client 2.配 ...

  7. linux mysql 数据库操作导入导出 数据表导出导入

    linux mysql 数据库操作导入导出 数据表导出导入 1,数据库导入 mysql -uroot -p show databases; create database newdb; use 数据库 ...

  8. 使用docker容器运行MySQL数据库并持久化数据文件

    1.下载mysql镜像 # docker pull mysql 2.启动mysql容器 # docker run -itd -v /data:/var/lib/mysql -p 33060:3306 ...

  9. mysql数据库优化方法大数据量查询轻松解决

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...

随机推荐

  1. Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就绪,挂起,运行) ,***协程概念,yield模拟并发(有缺陷),Greenlet模块(手动切换),Gevent(协程并发)

    Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就 ...

  2. Lucene搜索/索引过程笔记

    lucene索引文档过程: > 初始化IndexWriter > 构建Document > 调用IndexWriter.addDocument执行写入 > 初始化Documen ...

  3. maven-dependencies插件的模拟实现

    maven-dependencies插件的作用就是从本地的maven仓库中提取jar包,放到某个文件夹下面.这个功能其实是很简单的. 我在一家银行工作时,公司电脑都无法连外网,所以无法通过maven下 ...

  4. Vue.js项目实战-多语种网站(租车)

    首先来看一下网站效果,想写这个项目的读者可以自行下载哦,地址:https://github.com/Stray-Kite/Car: 在这个项目中,我们主要是为了学习语种切换,也就是右上角的 中文/En ...

  5. android studio学习----自动导包

    介绍一个最有用的设置,我们只有每次引用一些类的时候必须要导包,而Studio可以通过设置自动导包,简直太实用了. 到 Preferences -> Editor -> Auto Impor ...

  6. ANDROID培训准备资料之项目结构简单介绍

    Android Studio项目结构初步主要介绍下面几个文件夹,后续再补充 (1)java文件夹的介绍 (2)Res文件夹的介绍 (3)R文件的介绍 (4)Manifests文件夹的介绍 我们先看看整 ...

  7. Python从零开始——数值类型

  8. Python 简易的异步协程使用方法

    代码 import asyncio async def ex(id, n): print(id+" start") await asyncio.sleep(n/2) print(i ...

  9. PoI 3.17 已过时代码对比

    PoI 3.17 已过时代码对比颜色定义变化旧版本 : HSSFColor.BLACK.index新版本 : IndexedColors.BLACK.index 获取单元格格式旧版本 : cell.g ...

  10. Spring(005)-多环境Profile

    多个环境下的配置应该怎么进行,比如数据库连接字符,多个环境不同,spring的方案,大概总结如下. 例子,数据库配置. 定义一个获取数据库链接的接口 public interface DataConn ...