Spring Cloud实战 | 第七篇:完善ljf-admin服务,完成gateway基于角色权限认证
1、ljf-admin添加接口getUserDTOByUsername
/**
* @description: 获取userDTO
* @author: lijinfeng
* @date: 2021/12/14
* @param: [username]
* @return: com.ljf.common.base.vo.Result<com.ljf.admin.api.DTO.UserDTO>
*/
@GetMapping("/getUserDTOByUsername")
public Result<UserDTO> getUserDTOByUsername(@RequestParam String username){
//根据username获取SysUser对象
LambdaQueryWrapper<SysUser> userQueryWrapper = new LambdaQueryWrapper<SysUser>()
.eq(StringUtils.hasText(username), SysUser::getUsername,username);
SysUser user = sysUserService.getOne(userQueryWrapper);
log.info("获取user:"+user);
if (ObjectUtils.isEmpty(user))
return Result.error(ResultEnum.USER_NO_EXIST);
//根据userID获取SysUserRole对象
LambdaQueryWrapper<SysUserRole> serRoleQueryWrapper = new LambdaQueryWrapper<SysUserRole>()
.eq(SysUserRole::getUserId,user.getId());
List<SysUserRole> sysUserRoleList = sysUserRoleService.list(serRoleQueryWrapper);
log.info("获取用户角色关系sysUserRoleList:"+sysUserRoleList);
if (sysUserRoleList.size() == 0)
return Result.error(ResultEnum.USER_NO_EXIST_ROLE);
UserDTO userDTO = new UserDTO(user);
userDTO.setRoleIds(sysUserRoleList.stream().map(SysUserRole::getRoleId).collect(Collectors.toList()));
//根据roleId获取role对象
List<SysRole> roleList = sysRoleService.listByIds(userDTO.getRoleIds());
log.info("获取用户角色roleList:"+roleList);
userDTO.setRoles(roleList.stream().map(SysRole::getCode).collect(Collectors.toList()));
userDTO.setRoleNames(roleList.stream().map(SysRole::getName).collect(Collectors.toList()));
return Result.success(userDTO);
}
2、ljf-admin-api 添加feign接口
@GetMapping("/api/v1/admin/user/getUserDTOByUsername")
Result<UserDTO> getUserDTOByUsername(@RequestParam("username") String username);
3、ljf-auth修改UserDetailsServiceImpl类,完善认证登录用户信息
/**
* 【重要】从数据库获取用户信息,用于和前端传过来的用户信息进行密码判读
*/
@Service
@Slf4j
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserAdminFeign userAdminFeign;
@Autowired
private OAuthClientFeign oAuthClientFeign;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String clientId = JwtUtils.getAuthClientId();
Result<UserDTO> result;
OAuthUserDetails oauthUserDetails = null;
OauthClientDetails oauthClientDetails = oAuthClientFeign.getOAuthClientById(clientId).getData();
if (!ObjectUtils.isEmpty(oauthClientDetails)) {
result = userAdminFeign.getUserDTOByUsername(username);
if (ResultEnum.SUCCESS.getCode().equals(result.getCode())) {
UserDTO userDTO = result.getData();
oauthUserDetails = new OAuthUserDetails(userDTO);
System.out.println("ljf-auth:-----------------oauthUserDetails.toString():"+oauthUserDetails.toString());
}
} else {
throw new NoSuchClientException("该clientId不存在: " + clientId);
}
if (oauthUserDetails == null || oauthUserDetails.getId() == null) {
throw new UsernameNotFoundException(ResultEnum.USER_NO_EXIST.getMessage());
} else if (!oauthUserDetails.isEnabled()) {
throw new DisabledException("该账户已被禁用!");
} else if (!oauthUserDetails.isAccountNonLocked()) {
throw new LockedException("该账号已被锁定!");
} else if (!oauthUserDetails.isAccountNonExpired()) {
throw new AccountExpiredException("该账号已过期!");
}
return oauthUserDetails;
}
}
4、修改ljf-gateway的ResourceServerConfig类
重新定义权限管理器
http.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(jwtAuthenticationConverter())//重新定义权限管理器
.publicKey(rsaPublicKey());// 本地获取公钥
添加beanjwtAuthenticationConverter()
/**
* @return
* @link https://blog.youkuaiyun.com/qq_24230139/article/details/105091273
* ServerHttpSecurity没有将jwt中authorities的负载部分当做Authentication
* 需要把jwt的Claim中的authorities加入
* 方案:重新定义权限管理器,默认转换器JwtGrantedAuthoritiesConverter
*/
@Bean
public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
//设置前缀,默认为SCOPE_,级ROOT角色返回为SCOPE_ROOT,这里改成ROLE_ROOT
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
//设置认证的属性为自定义OAuthUserDetails中的authorities属性
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("authorities");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
}
5、添加鉴权管理器AuthorizationManager
/**
* @Auther: lijinfeng
* @Date: 2021/12/12
* @Description 描述:鉴权管理器
*/
@Component
@Slf4j
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
ServerHttpRequest request = authorizationContext.getExchange().getRequest();
// 1. 对应跨域的预检请求直接放行
if (request.getMethod() == HttpMethod.OPTIONS) {
return Mono.just(new AuthorizationDecision(true));
}
log.info("判断用户JWT中携带的角色是否有能通过权限拦截的角色");
// 判断用户JWT中携带的角色是否有能通过权限拦截的角色
Mono<AuthorizationDecision> authorizationDecisionMono = mono
.flatMapIterable(Authentication::getAuthorities)
.map(GrantedAuthority::getAuthority)
.any(authority -> {
log.info("用户权限(角色) : {}", authority); // ROLE_ROOT
// 截取ResourceServerConfig设置的自定义的ROLE_前缀
String role = authority.substring("ROLE_".length()); // ROOT
if ("ROOT".equals(role)) { // 如果是超级管理员则放行
return true;
}
// 其他角色暂时不让访问,以后添加条件后再具体判断
return false;
})
.map(AuthorizationDecision::new)
.defaultIfEmpty(new AuthorizationDecision(false));
return authorizationDecisionMono;
}
}
6、测试root用户和admin用户
admin显示访问未授权,root用户正常访问