SpringCloud微服务整合Spring Security OAuth2

这篇具有很好参考价值的文章主要介绍了SpringCloud微服务整合Spring Security OAuth2。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、前置知识

  • 首先得了解什么是OAuth2,这个的话可以参见博客:
    https://blog.csdn.net/weixin_42272869/article/details/112260123
    https://www.bilibili.com/video/BV1D94y1Z7t1?p=33&vd_source=bf9d70f3d2a451db07f40b6407c95a77
  • 本文采用的是使用最广泛的、安全性最高的授权码模式进行讲解。

2、思想

  • 单独创建一个鉴权微服务auth,负责整个项目的权限认证。外面请求进来先通过网关进行身份认证,未登录的去登录,已登录的网关找到auth进行鉴权,通过才放行到具体的普通功能性微服务。

  • 大体过程可用下图展示
    springcloud整合oauth2,微服务,spring,spring cloud

  • 权限管理呢,主要包含两个部分:认证和鉴权
    springcloud整合oauth2,微服务,spring,spring cloud

  • OAuth2的整体流程图:springcloud整合oauth2,微服务,spring,spring cloud
    (A)客户端向资源所有者请求授权。授权请求可以直接对资源所有者(如图所示)进行,或者通过授权服务器作为中介进行间接访问(首选方案)。
    (B)资源所有者允许授权,并返回凭证(如code)。
    (C)客户端通过授权服务器进行身份验证,并提供授权凭证(如code),请求访问令牌(access token)。
    (D)授权服务器对客户端进行身份验证,并验证授权凭证,如果有效,则发出访问令牌。
    (E)客户端向资源服务器请求受保护的资源,并通过提供访问令牌来进行身份验证。
    (F)资源服务器验证访问令牌,如果正确则返回受保护资源。

  • 简单说就是:客户应用向授权服务器请求Access Token —> 授权服务器向用户征询意见,是否将权限授予客户应用 —> 用户同意 —> 授权服务器生成颁发Access Token给客户应用 —> 客户应用拿着Access Token去请求资源服务器 —> 资源服务器验证客户应用的Access Token —> 验证通过,返回数据

2.1、认证

  • 说白了就是登录,之前比较传统的单点登录方法是使用session+redis,用户登录信息存放在服务端,压力比较大。
  • 所以,微服务中的认证,还是建议使用令牌的方式,可以选择 JWT 令牌,这也是目前使用较多的一种方案。但是熟悉 JWT 的小伙伴都知道,纯粹的无状态登录无法实现注销,这就很头大,所以在实际应用中,单纯的使用 JWT 是不行的,一般还是要结合 Redis 一起,将生成的 JWT 字符串在 Redis 上也保存一份,并设置过期时间,判断用户是否登录时,需要先去 Redis 上查看 JWT 字符串是否存在,存在的话再对 JWT 字符串做解析操作,如果能成功解析,就没问题,如果不能成功解析,就说明令牌不合法。
  • 这样有状态登录+无状态登录混在一起的方式,虽然看起来有点不伦不类,但是就当下来说,这个折衷的办法算是一个可行的方案了。
  • 但是这样JWT的优点(本身保存用户登录信息,不需要保存在服务端)就不复存在了,换句话说,跟分布式session也没啥两样了。
  • 这里不继续展开叙述,想了解JWT如何解决登录注销可以见博客:
  • 认证包含两个过程:校验和登录,请求过来先校验是否合法,不合法就去进行登录等操作来获取权限。

2.2、鉴权

在我这个demo里是提取出用户表中的用户权限,即便是登录了的用户也需要具有权限才能访问固定的资源。

3、步骤

3.1、auth端

3.1.1、微服务目录

springcloud整合oauth2,微服务,spring,spring cloud
其中整合了swagger的两个配置文件和本项目无关,可忽略

3.1.2、引入必要依赖

auth端:

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

还有一些必要的组件是放到了common模块,然后在auth端引入common模块就好了。比如有nacos,一些实体类等等。要记得引入common后也要做必要的配置,比如nacos,相关配置可见博客:https://blog.csdn.net/qq_41076797/article/details/128509393、https://blog.csdn.net/qq_41076797/article/details/128508723;这里就不详细说了。

3.1.3、配置用户鉴权实体类LoginUser

  • 用户已经有实体类User了,这里要对它进行封装,从鉴权的角度把User包一层。
  • 这里要实现UserDetails接口,实现其中的必要方法,这也属于固定套路,这样才能用于权限鉴定。
  • 在auth微服务下面开个detail文件夹,创建实体类LoginUser,
package com.lyy.yingwudemo.yingwu_auth.service;

/**
 * @author :lyy
 * @date : 04-06-10:15
 */
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;

/**
 * 登录用户身份权限
 *
 * @author ruoyi
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser implements UserDetails
{
   
    private static final long serialVersionUID = 1L;

    /**
     * 扩展字段
     */
    private Long userId;

    /**
     * 默认字段
     */
    private String username;
    private String password;
    private Boolean enabled;
    private Collection<SimpleGrantedAuthority> authorities;


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

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

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

    @Override
    public boolean isAccountNonExpired() {
   
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
   
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
   
        return true;
    }

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

3.1.4、创建根据用户名获取封装的用户信息的service:UserDetailServiceImpl

  • 这个主要就是定义一个类实现固定的接口UserDetailsService中的固定的方法loadUserByUsername,这个方法就是根据字符串username返回一个UserDetails,因为得让springsecurity直到你要进行鉴权的对象啥样啊!
  • 这里通过feign从用户服务member获取用户信息。
package com.lyy.yingwudemo.yingwu_auth.service;

import com.alibaba.fastjson.TypeReference;
import com.lyy.yingwuDemo.yingwu_common.entity.User;
import com.lyy.yingwuDemo.yingwu_common.utils.R;
import com.lyy.yingwudemo.yingwu_auth.feign.MemberFeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;

/**
 * @author :lyy
 * @date : 04-06-13:21
 */
@Service
@Slf4j
public class UserDetailServiceImpl implements UserDetailsService {
   

    @Autowired
    private MemberFeignService memberFeignService;

    /**
     *
     * @param username
     * @return 就是负责构建一个UserDetails,咱们之前构建的实体类LoginUser就实现了UserDetails,所以是符合要求的
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
   
        // 后面从管理端获取用户信息
        R r = memberFeignService.getMemberUsername(username);
        TypeReference<User> typeReference = new TypeReference<User>() {
   };
        User user=r.getData("user",typeReference);
        if(user==null)
            throw new UsernameNotFoundException("用户不存在");
        LoginUser userDetails = loadUser(user);
        if (!userDetails.isEnabled()) {
   
            throw new DisabledException("该账户已被禁用!");
        } else if (!userDetails.isAccountNonLocked()) {
   
            throw new LockedException("该账号已被锁定!");
        } else if (!userDetails.isAccountNonExpired()) {
   
            throw new AccountExpiredException("该账号已过期!");
        }
        return userDetails;
    }

    private LoginUser loadUser(User user) {
   
        Collection<SimpleGrantedAuthority> authorities =new ArrayList<>();
        user.getUserTags().stream().forEach(tag->
            authorities.add(new SimpleGrantedAuthority(tag.equals("1")?"ROLE_ADMIN":"ROLE_USER"))
        );

        LoginUser loginUser = new LoginUser();
        loginUser.setAuthorities(authorities);

        return LoginUser.builder()
                .userId(1L)
                .username(user.getUserName())
                .enabled(user.getEnable())
                .authorities(authorities)
                // 这里的密码就是正确密码,要拿前端传来的和下面的比较
                .password(new BCryptPasswordEncoder().encode(user.getPassWord())).build();
    }
}

3.1.5、如果不想自己设计用户service

不管要不要自己手动设计service,都要通过rpc调用,查询到username对应的那个用户,以及对应的权限
参考以下代码

@Service
@Slf4j
public class SecurityUserDetailService implements UserDetailsService {
   


    @Autowired
    private UserService userService;

    @Autowired
    private PermissionService permissionService;


    @Override
    public UserDetails loadUserByUsername(String username) {
   

        UserEntity user = userService.getUserByUsername(username);
        if (user == null) {
   
            return null;
        }
        //获取权限
        List<PermissionEntry> permissions = permissionService.getPermissionsByUserId(user.getId());
        List<String> codes = permissions.stream().map(PermissionEntry::getCode).collect(Collectors.toList());
        String[] authorities = null;
        if (CollectionUtils.isNotEmpty(codes)) {
   
            authorities = new String[codes.size()];
            codes.toArray(authorities);
        }
        //身份令牌
        String principal = JSON.toJSONString(user);
        return User.withUsername(principal).password(user.getPassword()).authorities(authorities).build();
    }
}

返回的User.withUsername……这个User是security包里的User,实现了UserDetail接口,只要返回的类是UserDetail的实现类即可,我们也可以自己实现。

3.1.6、创建客户端信息获取service

  • 所谓客户端:
    springcloud整合oauth2,微服务,spring,spring cloud
    我们可以这么理解:普通用户想访问B站的VIP资源,客户端就是这个普通用户。首先通过认证模块,检验该用户是否登录,登录后进行鉴权,判断是否有获取VIP资源的权力。

  • 再举个例子加深对客户端的理解:
    springcloud整合oauth2,微服务,spring,spring cloud

  • 看了好多博客的实现都是把客户端信息保存在内存里,比如下面的代码:
    springcloud整合oauth2,微服务,spring,spring cloud

  • 然而,关于认证客户端相关信息均存储在内存中,服务一旦重启,即随即丢失,非常不利于维护。在实际项目中,势必要持久化存储。

  • 这里要感谢https://blog.csdn.net/liuminglei1987/article/details/104363638
    提供的四种方法,这里采用第3种,因为后面的配置还需要一个ClientDetailsService类型的bean。代码会在后面给出,因为这段代码在后面的配置文件中。
    springcloud整合oauth2,微服务,spring,spring cloud
    这里就会从数据库去判断用户是否有权限访问资源了。对应的需要在数据库建立对应的表,不然security怎么拿到用户的权限等信息呢,其实就是把上面的内存中的客户端配置改成字段的形式保存在数据库。官方的github里也提供了数据库建表的语句:https://github.com/spring-attic/spring-security-oauth/blob/main/spring-security-oauth2/src/test/resources/schema.sql

  • 如果你不建表那就会报错:
    springcloud整合oauth2,微服务,spring,spring cloud

  • 这肯定不是自动建表的哈,因为客户端只有咱们自己知道。所以下面建表:

# client 相关信息
create table oauth_client_details
(
    client_id               VARCHAR(256) PRIMARY KEY comment '必填,Oauth2 client_id',
    resource_ids            VARCHAR(256) comment '可选,资源id集合,多个资源用英文逗号隔开',
    client_secret           VARCHAR(256) comment '必填,Oauth2 client_secret',
    scope                   VARCHAR(256) comment '必填,Oauth2 权限范围,比如 read,write等可自定义',
    authorized_grant_types  VARCHAR(256) comment '必填,Oauth2 授权类型,支持类型:authorization_code,password,refresh_token,implicit,client_credentials,多个用英文逗号隔开',
    web_server_redirect_uri VARCHAR(256) comment '可选,客户端的重定向URI,当grant_type为authorization_code或implicit时,此字段是需要的',
    authorities             VARCHAR(256) comment '可选,指定客户端所拥有的Spring Security的权限值',
    access_token_validity   INTEGER comment '可选,access_token的有效时间值(单位:秒),不填写框架(类refreshTokenValiditySeconds)默认12小时',
    refresh_token_validity  INTEGER comment '可选,refresh_token的有效时间值(单位:秒),不填写框架(类refreshTokenValiditySeconds)默认30天',
    additional_information  VARCHAR(4096) comment '预留字段,格式必须是json',
    autoapprove             VARCHAR(256) comment '该字段适用于grant_type="authorization_code"的情况下,用户是否自动approve操作'
);

3.1.7、添加授权服务相关配置AuthorizationServerConfig

这里配置了如下内容:

  • 设置oauth客户端 获取信息来源 clientDetailsService
  • 设置默认的token存储方式(后面改成redis存储)
  • 添加token增强器(在token中添加用户信息)
  • 添加token 加密方式

下面是简单版配置,没有配置token增强和加密,先跑通文章来源地址https://www.toymoban.com/news/detail-701460.html

package com.lyy.yingwudemo.yingwu_auth.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org

到了这里,关于SpringCloud微服务整合Spring Security OAuth2的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Spring Security Oauth2.1 最新版 1.1.0 整合 gateway 完成授权认证(拥抱 springboot 3.1)

    Spring Security Oauth2.1 最新版 1.1.0 整合 gateway 完成授权认证(拥抱 springboot 3.1)

    目录 背景 demo地址 版本 Spring Boot 3.1 Spring Authorization Server 1.1.0 基础 spring security OAuth2 模块构成 授权方式 认证方式 集成过程 官方demo 代码集成 依赖 授权服务AuthorizationServerConfig配置 重要组件 测试 查看授权服务配置 访问授权服务 授权 回调 获取 access_token 获取用户信息 个性

    2024年02月08日
    浏览(9)
  • 微服务安全Spring Security Oauth2实战

    微服务安全Spring Security Oauth2实战

    Spring Authorization Server 是一个框架,它提供了 OAuth 2.1 和 OpenID Connect 1.0 规范以及其他相关规范的实现。它建立在 Spring Security 之上,为构建 OpenID Connect 1.0 身份提供者和 OAuth2 授权服务器产品提供了一个安全、轻量级和可定制的基础。说白了,Spring Authorization Server 就是一个**认

    2024年02月03日
    浏览(10)
  • Spring Security Oauth2.1 最新版 1.1.0 整合 (基于 springboot 3.1.0)gateway 完成授权认证

    Spring Security Oauth2.1 最新版 1.1.0 整合 (基于 springboot 3.1.0)gateway 完成授权认证

    目录 背景 demo地址 版本 Spring Boot 3.1 Spring Authorization Server 1.1.0 基础 spring security OAuth2 模块构成 授权方式 认证方式 集成过程 官方demo 代码集成 依赖 授权服务AuthorizationServerConfig配置 重要组件 测试 查看授权服务配置 访问授权服务 授权 回调 获取 access_token 获取用户信息 个性

    2024年02月11日
    浏览(10)
  • Spring Security OAuth 2.0 资源服务器— JWT

    Spring Security OAuth 2.0 资源服务器— JWT

    目录 一、JWT的最小依赖 二、JWT的最基本配置 1、指定授权服务器 2、初始预期(Startup Expectations) 3、运行时预期(Runtime Expectations) 三、JWT认证是如何工作的 四、直接指定授权服务器 JWK Set Uri 五、提供 audiences 六、覆盖或取代启动自动配置 1、使用jwkSetUri() 2、使用decoder()

    2024年02月05日
    浏览(8)
  • 搭建spring security oauth2认证授权服务器

    搭建spring security oauth2认证授权服务器

    下面是在spring security项目的基础上搭建spring security oauth2认证授权服务器 spring security oauth2认证授权服务器主要需要以下依赖 Spring Security对OAuth2默认可访问端点 ​/oauth/authorize​ ​​:申请授权码code,涉及类​ ​AuthorizationEndpoint​ ​ ​/oauth/token​ ​​:获取令牌token,涉及类​

    2024年01月21日
    浏览(10)
  • Spring Authorization Server入门 (八) Spring Boot引入Security OAuth2 Client对接认证服务

    Spring Authorization Server入门 (八) Spring Boot引入Security OAuth2 Client对接认证服务

    在之前的文章中实现了一个认证服务,并且添加了一些自定义的内容,现在暂时没想到认证服务的新内容,本篇文章就先写一下客户端对接的吧,水一篇。 当用户通过客户端去访问一个受限的资源时,客户端会检测是否有登录信息,没有登录信息会重定向至认证服务器去请求

    2024年02月21日
    浏览(14)
  • 【Spring Boot Admin】使用(整合Spring Security服务,添加鉴权)

    【Spring Boot Admin】使用(整合Spring Security服务,添加鉴权)

    Spring Boot Admin 监控平台 背景:Spring Boot Admin 监控平台不添加鉴权就直接访问的话,是非常不安全的。所以在生产环境中使用时,需要添加鉴权,只有通过鉴权后才能监控客户端服务。本文整合Spring Security进行实现。 pom依赖 yml配置 启动类@EnableAdminServer 安全配置类:SecuritySe

    2024年02月16日
    浏览(20)
  • Spring Security OAuth2.0(3):Spring Security简单入门

    Spring Security OAuth2.0(3):Spring Security简单入门

    Spring Security 快速入门。 本章代码已分享至Gitee:https://gitee.com/lengcz/security-spring-security qquad Spring Secutiry 是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。由于它是Spring生态系统的一员,因此它伴随着整个Spring生态系统不断修正、升级,

    2024年02月13日
    浏览(13)
  • Spring Security OAuth2.0(5):Spring Security工作原理

    Spring Security OAuth2.0(5):Spring Security工作原理

    qquad Spring Security 所解决的问题就是安全访问控制,而安全访问控制功能其实就是所有进入系统的请求进行拦截,校验每个请求是否能够访问它所期望的资源。Spring Security 对Web资源的保护是通过Filter入手的,所以从这个Filter入手,逐步深入Spring Security原理。 $qquad%当初始化

    2024年02月17日
    浏览(12)
  • Spring Security OAuth 2.0

    Spring Security OAuth 2.0

    1 概念 OAuth 2.0 到底是什么呢?我们先从字面上来分析下。OAuth 2.0 一词中的字母 “O” 是 Open 的简称,表示 “开放” , “Auth” 表示 “授权”,连在一起就表示 “开放授权”。 OAuth 2.0是一种授权框架,提供了一套规范和协议,用于实现授权流程和访问令牌的管理,而非单个

    2024年02月16日
    浏览(11)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包