• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

SpringCloud微服务整合Spring Security进行统一鉴权

武飞扬头像
CtrlZ1
帮助2

1、思想

  • 网关gateway
  • 普通资源微服务member
  • 鉴权微服务auth
    为了做到更灵活的方法级别的鉴权操作,决定将权限控制下放到具体的普通微服务,其实并不需要多配置很多东西。网关只负责转发请求,鉴权则是由auth认证微服务来完成的。
  • 网上很多都是在网关层面进行鉴权,但是这么做不灵活,并不能做到方法级别的鉴权。比如,普通资源微服务中的一些方法并不需要权限,是开放访问的,而有些方法是需要鉴权的。网关层面只能决定某个微服务是否需要鉴权,要么所有方法全做,要么全不做,是缺少灵活度的。

2、步骤

2.1、前言

  • 有一个大坑,记得如果是单机操作多个微服务项目,要给每个微服务添加session cookie name,如下
server:
  port: 8003
  servlet:
    session:
      cookie:
        #防止Cookie冲突,冲突会导致登录验证不通过
        name: OAUTH2-CLIENT-SESSIONID03
  • 一定一定要做这一步。

2.2、关系

  • 网关gateway
  • 普通资源微服务member
  • 鉴权微服务auth

2.3、认证微服务auth

  • 不需要变动,参考我之前系列的博客:https://blog.csdn.net/qq_41076797/article/details/129985272

2.3.1、微服务目录

学新通
其中整合了swagger的两个配置文件和本项目无关,可忽略

2.3.2、引入必要依赖

auth端:

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

2.3.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;
    }
}
学新通

2.3.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();
    }
}

学新通

2.3.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());
        Strin
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgaaccg
系列文章
更多 icon
同类精品
更多 icon
继续加载