Spring Cloud OAuth2(一) 搭建授权服务

2023-11-03

本文内容主要为spring cloud 授权服务的搭建,采用jwt认证。
GitHub 地址:https://github.com/fp2952/spring-cloud-base/tree/master/auth-center/auth-center-provider

添加依赖

Spring Security 及 Security 的OAuth2 扩展

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

启动类注解

启动类添加 @EnableAuthorizationServer 注解

@SpringCloudApplication
@EnableAuthorizationServer
@EnableFeignClients("com.peng.main.client")
public class AuthCenterProviderApplication {
   public static void main(String[] args){
       SpringApplication.run(AuthCenterProviderApplication.class, args);
   }
}

Oauth3配置类AuthorizationServerConfigurerAdapter

AuthorizationServerConfigurerAdapter中:

  • ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
  • AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束.
  • AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。
    主要配置如下:

配置客户端详情信息(Client Details)

ClientDetailsServiceConfigurer (AuthorizationServerConfigurer 的一个回调配置项) 能够使用内存或者JDBC来实现客户端详情服务(ClientDetailsService),Spring Security OAuth2的配置方法是编写@Configuration类继承AuthorizationServerConfigurerAdapter,然后重写void configure(ClientDetailsServiceConfigurer clients)方法,如:

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 使用JdbcClientDetailsService客户端详情服务
        clients.withClientDetails(new JdbcClientDetailsService(dataSource));
    }

这里使用Jdbc实现客户端详情服务,数据源dataSource不做叙述,使用框架默认的表,schema链接:
https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql

配置令牌 管理 (jwtAccessTokenConverter)

JwtAccessTokenConverter是用来生成token的转换器,而token令牌默认是有签名的,且资源服务器需要验证这个签名。此处的加密及验签包括两种方式:
对称加密、非对称加密(公钥密钥)
对称加密需要授权服务器和资源服务器存储同一key值,而非对称加密可使用密钥加密,暴露公钥给资源服务器验签,本文中使用非对称加密方式,配置于AuthorizationServerConfigurerAdapter如下:

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager)
                // 配置JwtAccessToken转换器
                .accessTokenConverter(jwtAccessTokenConverter())
                // refresh_token需要userDetailsService
                .reuseRefreshTokens(false).userDetailsService(userDetailsService);
                //.tokenStore(getJdbcTokenStore());
    }

    /**
     * 使用非对称加密算法来对Token进行签名
     * @return
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {

        final JwtAccessTokenConverter converter = new JwtAccessToken();
        // 导入证书
        KeyStoreKeyFactory keyStoreKeyFactory =
                new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), "mypass".toCharArray());
        converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mytest"));

        return converter;
    }

通过 JDK 工具生成 JKS 证书文件,并将 keystore.jks 放入resource目录下
keytool -genkeypair -alias mytest -keyalg RSA -keypass mypass -keystore keystore.jks -storepass mypass

此处我们自定义JwtAccessToken用于添加额外用户信息

/**
 * Created by fp295 on 2018/4/16.
 * 自定义JwtAccessToken转换器
 */
public class JwtAccessToken extends JwtAccessTokenConverter {

    /**
     * 生成token
     * @param accessToken
     * @param authentication
     * @return
     */
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        DefaultOAuth2AccessToken defaultOAuth2AccessToken = new DefaultOAuth2AccessToken(accessToken);

        // 设置额外用户信息
        BaseUser baseUser = ((BaseUserDetail) authentication.getPrincipal()).getBaseUser();
        baseUser.setPassword(null);
        // 将用户信息添加到token额外信息中
        defaultOAuth2AccessToken.getAdditionalInformation().put(Constant.USER_INFO, baseUser);

        return super.enhance(defaultOAuth2AccessToken, authentication);
    }

    /**
     * 解析token
     * @param value
     * @param map
     * @return
     */
    @Override
    public OAuth2AccessToken extractAccessToken(String value, Map<String, ?> map){
        OAuth2AccessToken oauth2AccessToken = super.extractAccessToken(value, map);
        convertData(oauth2AccessToken, oauth2AccessToken.getAdditionalInformation());
        return oauth2AccessToken;
    }

    private void convertData(OAuth2AccessToken accessToken,  Map<String, ?> map) {
        accessToken.getAdditionalInformation().put(Constant.USER_INFO,convertUserData(map.get(Constant.USER_INFO)));

    }

    private BaseUser convertUserData(Object map) {
        String json = JsonUtils.deserializer(map);
        BaseUser user = JsonUtils.serializable(json, BaseUser.class);
        return user;
    }
}

JwtAccessToken 类中从authentication里的getPrincipal(实际为UserDetails接口)获取用户信息,所以我们需要实现自己的UserDetails

/**
 * Created by fp295 on 2018/4/29.
 * 包装org.springframework.security.core.userdetails.User类
 */
public class BaseUserDetail implements UserDetails, CredentialsContainer {

    private final BaseUser baseUser;
    private final org.springframework.security.core.userdetails.User user;

    public BaseUserDetail(BaseUser baseUser, User user) {
        this.baseUser = baseUser;
        this.user = user;
    }


    @Override
    public void eraseCredentials() {
        user.eraseCredentials();
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return user.getAuthorities();
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return user.isAccountNonExpired();
    }

    @Override
    public boolean isAccountNonLocked() {
        return user.isAccountNonLocked();
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return user.isCredentialsNonExpired();
    }

    @Override
    public boolean isEnabled() {
        return user.isEnabled();
    }

    public BaseUser getBaseUser() {
        return baseUser;
    }
}

授权端点开放

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        oauthServer
                // 开启/oauth/token_key验证端口无权限访问
                .tokenKeyAccess("permitAll()")
                // 开启/oauth/check_token验证端口认证权限访问
                .checkTokenAccess("isAuthenticated()");
    }

Security 配置

需要配置 DaoAuthenticationProvider、UserDetailService 等

@Configuration
@Order(ManagementServerProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    // 自动注入UserDetailsService
    @Autowired
    private BaseUserDetailService baseUserDetailService;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http    // 配置登陆页/login并允许访问
                .formLogin().permitAll()
                // 登出页
                .and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
                // 其余所有请求全部需要鉴权认证
                .and().authorizeRequests().anyRequest().authenticated()
                // 由于使用的是JWT,我们这里不需要csrf
                .and().csrf().disable();
    }

    /**
     * 用户验证
     * @param auth
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(daoAuthenticationProvider());
    }


    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(){
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        // 设置userDetailsService
        provider.setUserDetailsService(baseUserDetailService);
        // 禁止隐藏用户未找到异常
        provider.setHideUserNotFoundExceptions(false);
        // 使用BCrypt进行密码的hash
        provider.setPasswordEncoder(new BCryptPasswordEncoder(6));
        return provider;
    }
}

UserDetailsService 实现

@Service
public class BaseUserDetailService implements UserDetailsService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private BaseUserService baseUserService;
    @Autowired
    private BaseRoleService baseRoleService;
 
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 调用FeignClient查询用户
        ResponseData<BaseUser> baseUserResponseData = baseUserService.getUserByUserName(username);
        if(baseUserResponseData.getData() == null || !ResponseCode.SUCCESS.getCode().equals(baseUserResponseData.getCode())){
            logger.error("找不到该用户,用户名:" + username);
            throw new UsernameNotFoundException("找不到该用户,用户名:" + username);
        }
        BaseUser baseUser = baseUserResponseData.getData();

        // 调用FeignClient查询角色
        ResponseData<List<BaseRole>> baseRoleListResponseData = baseRoleService.getRoleByUserId(baseUser.getId());
        List<BaseRole> roles;
        if(baseRoleListResponseData.getData() == null ||  !ResponseCode.SUCCESS.getCode().equals(baseRoleListResponseData.getCode())){
            logger.error("查询角色失败!");
            roles = new ArrayList<>();
        }else {
            roles = baseRoleListResponseData.getData();
        }

        // 获取用户权限列表
        List<GrantedAuthority> authorities = new ArrayList();
        roles.forEach(e -> {
            // 存储用户、角色信息到GrantedAuthority,并放到GrantedAuthority列表
            GrantedAuthority authority = new SimpleGrantedAuthority(e.getRoleCode());
            authorities.add(authority);
        
        });

        // 返回带有用户权限信息的User
        org.springframework.security.core.userdetails.User user =  new org.springframework.security.core.userdetails.User(baseUser.getUserName(),
                baseUser.getPassword(), isActive(baseUser.getActive()), true, true, true, authorities);
        return new BaseUserDetail(baseUser, user);
    }

    private boolean isActive(int active){
        return active == 1 ? true : false;
    }
}

授权服务器验证

http://127.0.0.1:8080/oauth/authorize?client_id=clientId&response_type=code&redirect_uri=www.baidu.com

注意:client_id:为存储在数据库里的client_id, response_type:写死code

  1. 链接回车后进入spring security 的简单登陆页面,

  1. 输入账号密码,为实现的 UserDetailsService 要里获取的用户,点击 login,
  2. 进入简单授权页面,点击 Authorize,
  3. 重定向到 redirect_uri,并带有 code 参数:
    http://www.baidu.com?code=rTKETX
  4. post请求获取 token:

注意,此处需加 Authorization 请求头,值为 Basic xxx xxx 为 client_id:client_secret 的 base64编码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Cloud OAuth2(一) 搭建授权服务 的相关文章

  • 在 jTextfield 中禁用“粘贴”

    我有一个用 Swing awt 编写的应用程序 我想阻止用户将值粘贴到文本字段中 有没有办法在不使用动作监听器的情况下做到这一点 您可以使用 null 参数调用 setTransferHandler 如下所示 textComponent s
  • 使类只能从特定类实例化

    假设我有 3 节课class1 class2 and class3 我怎样才能拥有它class1只能通过实例化class2 class1 object new class1 但不是 class3 或任何其他类 我认为它应该与修饰符一起使用
  • 最快的高斯模糊实现

    如何以最快的速度实施高斯模糊 http en wikipedia org wiki Gaussian blur算法 我要用Java来实现它 所以GPU http en wikipedia org wiki Graphics processi
  • 如何在 MSSQL 中获取 CURRENT_DATE?

    我正在使用 jpa 3 o 和 Hibernate 我有一个命名查询 SELECT COUNT wt id FROM WPSTransaction wt WHERE wt createdDate gt CURRENT DATE WPSTra
  • JavaFX使节点覆盖父节点边框颜色

    我有一个如下所示的节点 仅使用 css 我希望标签覆盖其父边框颜色 因此标签下方的边框颜色部分变得不可见 我用来制作这个边框的CSS代码 fx border color black fx border width 3 fx border r
  • java setFullScreenWindow 在 Mac 中隐藏登录对话框

    我使用的是全屏窗口 类似于屏幕保护程序 使用这里的方法 GraphicsEnvironment getLocalGraphicsEnvironment getDefaultScreenDevice setFullScreenWindow t
  • 如何在 JPA 和 Hibernate 中将数据库生成的列值定义为只读字段?

    使用 MariaDB 10 2 可以定义日期时间的默认值 例如创建和最后修改 我应该如何将此列作为只读字段访问 因为这个值应该只在数据库的控制之下 并且不应该从代码中修改 但我想在代码中读取这个属性 这很简单 只需设置insertable
  • 生成 equals 和 hashcode 时忽略属性

    假设我有一个类 Customer public class Customer private String firstName private String lastName private String doNotAddMeToEqual
  • JUNIT 测试 void 方法

    我有一个充满 void 方法的 java 类 我想进行一些单元测试以获得最大的代码覆盖率 例如我有这个方法 protected static void checkifValidElements int arg1 int arg2 metho
  • 在 java 中运行外部应用程序但不要等待它完成

    我正在用java编写一个应用程序 允许我运行其他应用程序 为此 我使用了 Process 类对象 但当我这样做时 应用程序会等待进程结束 然后再退出 有没有办法在 Java 中运行外部应用程序 但不等待它完成 public static v
  • 如何从字符串中解析一个大整数? [复制]

    这个问题在这里已经有答案了 我有一个这样的方法 Integer parseInt myInt 不是这个整数变得很长 我得到以下异常 java lang NumberFormatException For input string 40001
  • Java 中 JButton 的击键/热键

    最初我使用 JMenu 并建立热键以使用加速器工作 它运行得很好 现在我想在 JButton 中实现相同的行为 但我陷入困境 这是我编写的代码 请分享您的想法 以便我可以走上正确的道路 import javax swing import j
  • C 与 C++ 中的 JNI 调用不同?

    所以我有以下使用 Java 本机接口的 C 代码 但是我想将其转换为 C 但不知道如何转换 include
  • 当底层连接是有状态时如何使用 Apache HttpClient?

    我在谷歌上搜索了很多关于如何使用 HttpClient 进行多线程处理的信息 他们中的大多数人建议使用 ThreadSafeClientConnManager 但我的应用程序必须登录某个主机 登录表单页面 以便 HttpClient 获得底
  • 如何使用Gson仅从Json反序列化某些特定字段?

    我有以下 JSON 字符串 channel bvmt initValues data value instrumentIds TN0007250012 TN0007500010 instruments mnemonic ADWYA marc
  • 受信任的 1.5 小程序可以执行系统命令吗?

    如果是的话 这个能力有什么限制吗 具体来说 我需要以 Mac OSX 为目标 我以前用过这个在 Windows 系统上启动东西 但从未在 Mac 上尝试过 public void launchScript String args Strin
  • 如何使用自定义 JDK 构建 Jenkins 项目?

    我有一个常规的 Jenkins 实例 运行一些多分支管道 该实例在 JDK 11 上运行 因为 Jenkins 并不真正支持更高版本 没关系 但不好的是 我的所有管道似乎也都受到 Java 11 的限制 Jenkins 仅使用它自己也使用的
  • Java/MongoDB 按日期查询

    我将一个值作为 java util Date 存储在我的集合中 但是当我查询以获取两个特定日期之间的值时 我最终得到的值超出了范围 这是我的代码 插入 BasicDBObject object new BasicDBObject objec
  • 为什么java.lang.Cloneable不重写java.lang.Object中的clone()方法?

    Java 规范java lang Cloneable接口将自身定义为表示扩展它的任何对象也实现了clone 休眠的方法java lang Object 具体来说 它说 一个类实现了Cloneable接口来指示java lang Object
  • Errors/BindingResult 参数应在模型属性、@RequestBody 或 @RequestPart 参数之后立即声明

    我通过剖析示例应用程序来自学 Spring 然后到处添加代码来测试我在剖析过程中开发的理论 在测试添加到 Spring 应用程序中的一些代码时 我收到以下错误消息 An Errors BindingResult argument is ex

随机推荐

  • android模拟器Unexpected value from nativeGetEnabledTags: 0 问题解决方法

    在android4 2的模拟器上有一个问题 就是在运行模拟器的时候 老是会弹出Unexpected value from nativeGetEnabledTags 0提示 查询后得知是sdk的一个bug 虽然不影响使用 但是看着还是不爽 解
  • 表单嵌套表格,实现表格行内表单的校验

    随着element ui的使用 很多业务场景的实现 往往需要多个组件嵌套使用来实现 前段时间 为了实现表格中的每一行的数据进行校验的同时实现添加多个的需求 初步想法 在每一行对应属性中嵌套表单实现逐个校验 但是由于数据量大大 一行需要展示的
  • 解决Establishing SSL connection without server‘s identity verification is not recommended.

    每次从数据库中进行查询或者其他操作控制台都会出现以下警告 虽说不是error 但是很显眼 Establishing SSL connection without server s identity verification is not r
  • 03-分布式文件系统HDFS

    目录 一 HDFS简介 1 什么是HDFS 2 优缺点 2 1 优点 2 2 缺点 二 HDFS原理 1 系统架构 1 1 角色分工 1 2 设计思想 2 数据存储 2 1 文件存储 2 2 元数据存储 3 读写操作 3 1 写操作 3 2
  • 智慧城市篇

    智慧城市篇 数字孪生智慧排水管网管理平台https mp weixin qq com s ZDgmKqHRztYk2ehBDbi3AA 2022年3月1日 住房和城乡建设部印发了 十四五 住房和城乡建设科技发展规划 提出关于实现城市基础设施
  • Mybatis中的resultType和resultMap

    一 概述 MyBatis中在查询进行select映射的时候 返回类型可以用resultType 也可以用resultMap resultType是直接表示返回类型的 而resultMap则是对外部ResultMap的引用 但是resultT
  • android组件悬浮,Andorid 任意界面悬浮窗,实现悬浮窗如此简单

    特性 1 支持拖动 提供自动贴边等动画 2 内部自动进行权限申请操作 3 可自由指定要显示悬浮窗的界面 4 应用退到后台时 悬浮窗会自动隐藏 5 位置不可变的悬浮窗无需权限申请 6 位置及宽高可设置百分比值 轻松适配各分辨率 7 链式调用
  • [Python / PyTorch] debug backward()

    问题描述 在自定义Loss的中 其backward 函数不支持在PyCharm中进行断点调试 因此需要以其他方式进行断点调试 解决方案 参考 Is there a way to debug the backward method of Fu
  • SQLI-Labs(3)8-14关【布尔盲注和时间盲注】

    目录 第八关 第九关 第十关 第十一关 第十二关 第十三关 第十四关 第八关 我们用测试语句来测试是否为注入点 从上图中得知存在注入点 那么接下来就是爆列 一共有三列 接下来用union select 和报错注入都试一下发现没有回显点 那么
  • thinkPHP使用PHPExcel实现导入导出

    目录 一 使用composer安装PHPExcel 二 使用PHPExcel 1 导入Excel文件 2 导出数据 3 导出方法使用demo 效果图 一 使用composer安装PHPExcel 安装命令 composer require
  • 常见的PLC通讯协议有哪些?

    PLC 可编程逻辑控制器 通讯方式有多种 以下是一些常见的通讯方式 串口通信 使用串行接口 如RS232 RS485等 进行通信 常用于与外部设备进行简单的数据传输 以太网通信 通过以太网接口进行通信 可以实现较高的数据传输速率和远程连接
  • 键值数据库PebblesDB读后感

    键值数据库PebblesDB读后感 在LevelDB RocksDB这种分层思路上 PebblesDB提出了一种减少写放大的思路 下面学习并总结 所述以论文为基础 也有个人 观点 客观论述请看原文 虽然LSM的写放大最近被研究很多 但是就写
  • 关于log4j

    log4j 在强调可重用组件开发的今天 除了自己从头到尾开发一个可重用的日志操作类外 Apache为我们提供了一个强有力的日志操作包 Log4j 官方站点 http logging apache org log4j Log4j是Apache
  • linux拒绝更改密码,【Linux】解决SSH服务拒绝密码

    xShell连接Linux服务器提示密码错误 1 检查虚拟机SSH服务是否开启 service sshd status 如果没有开启 请执行service sshd start启动该服务 或者通过service sshd restart重启
  • web3.0 nft 是什么? nft的意义是什么?

    英国一名12岁的男孩本雅明 他在暑假期间画了一系列的画作 并在网上以数字藏品的形式进行出售 不到9小时就全部售完 赚取的虚拟货币价值相当于250万人民币 这些画算不上很高端的艺术佳作 而是由密密麻麻的像素组成各种形状各异的鲸鱼 每条鲸鱼都有
  • Vulhub漏洞靶场搭建和使用

    今天继续给大家介绍渗透测试相关知识 本文主要内容是Vulhub漏洞靶场搭建和使用 免责声明 本文所介绍的内容仅做学习交流使用 严禁利用文中技术进行非法行为 否则造成一切严重后果自负 再次强调 严禁对未授权设备进行渗透测试 一 Vulhub漏
  • 视频播放器测试点

    视频播放器测试点 在负责XX项目组的测试中 接触了好多的关于播放器的测试 基于这些 再结合我测试过程中遇到的问题整理的测试点分别从以下几个方面进行 功能测试 视频资源可以正常获取 不管是服务器返回还是后台添加等 视频的封面图 页面UI等正常
  • 【转载】关于在keil5的time environment没有StdPeriph Drivers(标准库)但是又想使用库函数的解决办法

    关于在keil5的time environment没有StdPeriph Drivers 标准库 但是又想使用库函数的解决办法 本人刚刚接触keil5 遇到了一些问题 希望我的方法能帮到大家 作者本人使用的是芯片是stm32f407VET6
  • SpringBoot配置动态定时任务

    1 配置ScheduledTask 主要是实现SchedulingConfigurer 动态传入cron package com hzl boot config import lombok Data import org springfra
  • Spring Cloud OAuth2(一) 搭建授权服务

    本文内容主要为spring cloud 授权服务的搭建 采用jwt认证 GitHub 地址 https github com fp2952 spring cloud base tree master auth center auth cen