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>
还有一些必要的组件是放到了common模块,然后在auth端引入common模块就好了。比如有nacos,一些实体类等等。要记得引入common后也要做必要的配置,比如nacos,相关配置可见博客:https://blog.csdn.net/qq_41076797/article/details/128509393、https://blog.csdn.net/qq_41076797/article/details/128508723;这里就不详细说了。文章来源:https://www.toymoban.com/news/detail-764046.html
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对应的那个用户,以及对应的权限。
参考以下代码文章来源地址https://www.toymoban.com/news/detail-764046.html
@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
到了这里,关于SpringCloud微服务整合Spring Security进行统一鉴权的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!