1、思想
- 網(wǎng)關(guān)gateway
- 普通資源微服務(wù)member
- 鑒權(quán)微服務(wù)auth
為了做到更靈活的方法級別的鑒權(quán)操作,決定將權(quán)限控制下放到具體的普通微服務(wù),其實并不需要多配置很多東西。網(wǎng)關(guān)只負責轉(zhuǎn)發(fā)請求,鑒權(quán)則是由auth認證微服務(wù)來完成的。 - 網(wǎng)上很多都是在網(wǎng)關(guān)層面進行鑒權(quán),但是這么做不靈活,并不能做到方法級別的鑒權(quán)。比如,普通資源微服務(wù)中的一些方法并不需要權(quán)限,是開放訪問的,而有些方法是需要鑒權(quán)的。網(wǎng)關(guān)層面只能決定某個微服務(wù)是否需要鑒權(quán),要么所有方法全做,要么全不做,是缺少靈活度的。
2、步驟
2.1、前言
- 有一個大坑,記得如果是單機操作多個微服務(wù)項目,要給每個微服務(wù)添加session cookie name,如下
server:
port: 8003
servlet:
session:
cookie:
#防止Cookie沖突,沖突會導致登錄驗證不通過
name: OAUTH2-CLIENT-SESSIONID03
- 一定一定要做這一步。
2.2、關(guān)系
- 網(wǎng)關(guān)gateway
- 普通資源微服務(wù)member
- 鑒權(quán)微服務(wù)auth
2.3、認證微服務(wù)auth
- 不需要變動,參考我之前系列的博客:https://blog.csdn.net/qq_41076797/article/details/129985272
2.3.1、微服務(wù)目錄
其中整合了swagger的兩個配置文件和本項目無關(guān),可忽略。
2.3.2、引入必要依賴
auth端:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
還有一些必要的組件是放到了common模塊,然后在auth端引入common模塊就好了。比如有nacos,一些實體類等等。要記得引入common后也要做必要的配置,比如nacos,相關(guān)配置可見博客:https://blog.csdn.net/qq_41076797/article/details/128509393、https://blog.csdn.net/qq_41076797/article/details/128508723;這里就不詳細說了。文章來源:http://www.zghlxwxcb.cn/news/detail-764046.html
2.3.3、配置用戶鑒權(quán)實體類LoginUser
- 用戶已經(jīng)有實體類User了,這里要對它進行封裝,從鑒權(quán)的角度把User包一層。
- 這里要實現(xiàn)UserDetails接口,實現(xiàn)其中的必要方法,這也屬于固定套路,這樣才能用于權(quán)限鑒定。
- 在auth微服務(wù)下面開個detail文件夾,創(chuàng)建實體類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;
/**
* 登錄用戶身份權(quán)限
*
* @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、創(chuàng)建根據(jù)用戶名獲取封裝的用戶信息的service:UserDetailServiceImpl
- 這個主要就是定義一個類實現(xiàn)固定的接口UserDetailsService中的固定的方法loadUserByUsername,這個方法就是根據(jù)字符串username返回一個UserDetails,因為得讓springsecurity直到你要進行鑒權(quán)的對象啥樣啊!
- 這里通過feign從用戶服務(wù)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 就是負責構(gòu)建一個UserDetails,咱們之前構(gòu)建的實體類LoginUser就實現(xiàn)了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、如果不想自己設(shè)計用戶service
不管要不要自己手動設(shè)計service,都要通過rpc調(diào)用,查詢到username對應的那個用戶,以及對應的權(quán)限。
參考以下代碼文章來源地址http://www.zghlxwxcb.cn/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;
}
//獲取權(quán)限
List<PermissionEntry> permissions = permissionService.getPermissionsByUserId(user.getId());
List<String> codes = permissions.stream().map(PermissionEntry::getCode).collect(Collectors
到了這里,關(guān)于SpringCloud微服務(wù)整合Spring Security進行統(tǒng)一鑒權(quán)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!