feat:实现全异步改造

This commit is contained in:
wangyu 2021-12-07 22:43:03 +08:00
parent 72550ab68e
commit a5ffeae42f
26 changed files with 500 additions and 152 deletions

View File

@ -0,0 +1,24 @@
package com.flyfish.framework.auditor;
import com.flyfish.framework.context.UserContext;
import com.flyfish.framework.domain.authorized.AuthorizedDomain;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class AuthorizeAuditor implements BeanAuditor<AuthorizedDomain> {
private final UserContext userContext;
/**
* 对实体进行审查并补全相关字段
*
* @param data 原数据
* @return 结果
*/
@Override
public void audit(AuthorizedDomain data) {
data.setAuthorizeId(userContext.currentUser().getAuthority());
}
}

View File

@ -0,0 +1,29 @@
package com.flyfish.framework.auditor;
import com.flyfish.framework.domain.authorized.AuthorizedDomain;
import com.flyfish.framework.domain.base.IUser;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Component
@RequiredArgsConstructor
public class ReactiveAuthorizeAuditor implements ReactiveBeanAuditor<AuthorizedDomain> {
/**
* 对实体进行审查并补全相关字段
*
* @param data 原数据
* @return 结果
*/
@Override
public Mono<AuthorizedDomain> audit(AuthorizedDomain data) {
return ReactiveSecurityContextHolder.getContext()
.map(ctx -> (IUser) ctx.getAuthentication().getPrincipal())
.map(user -> {
data.setAuthorizeId(user.getAuthority());
return data;
});
}
}

View File

@ -0,0 +1,37 @@
package com.flyfish.framework.domain.authorized;
import com.flyfish.framework.builder.CriteriaBuilder;
import com.flyfish.framework.domain.base.NameLikeQo;
import com.flyfish.framework.domain.po.Department;
import lombok.Getter;
import lombok.Setter;
import java.util.Collections;
import java.util.List;
/**
* 带鉴权的查询实体主要以部门隔绝
*
* @param <T>
*/
@Getter
@Setter
public abstract class AuthorizedQo<T extends AuthorizedDomain> extends NameLikeQo<T> {
/**
* 获取可见的权限ids
*
* @return 结果
*/
public List<String> getAuthorizedIds() {
if (user instanceof AuthorizedUserDetails) {
return ((AuthorizedUserDetails) user).getAuthorityCodes();
}
return Collections.singletonList(Department.PUBLIC);
}
@Override
public CriteriaBuilder<T> criteriaBuilder() {
return super.criteriaBuilder().with("authorizedIds", "authorizeId", CriteriaBuilder.Builders.IN);
}
}

View File

@ -0,0 +1,18 @@
package com.flyfish.framework.domain.authorized;
import java.util.List;
/**
* 支持授权的用户详情
*
* @author wangyu
*/
public interface AuthorizedUserDetails {
/**
* 获取授权codes
*
* @return 结果
*/
List<String> getAuthorityCodes();
}

View File

@ -2,7 +2,6 @@ package com.flyfish.framework.domain.base;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.flyfish.framework.builder.CriteriaBuilder; import com.flyfish.framework.builder.CriteriaBuilder;
import com.flyfish.framework.domain.po.User;
import org.springframework.data.domain.Example; import org.springframework.data.domain.Example;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
@ -22,7 +21,7 @@ public class BaseQo<T extends Domain> implements Qo<T> {
protected List<T> result; protected List<T> result;
protected User user; protected IUser user;
public Qo<T> accept(List<T> result, Pageable pageable) { public Qo<T> accept(List<T> result, Pageable pageable) {
this.pageable = pageable; this.pageable = pageable;
@ -52,7 +51,7 @@ public class BaseQo<T extends Domain> implements Qo<T> {
* @return 结果 * @return 结果
*/ */
@Override @Override
public User getUser() { public IUser getUser() {
return user; return user;
} }
@ -62,7 +61,7 @@ public class BaseQo<T extends Domain> implements Qo<T> {
* @param user 用户数据 * @param user 用户数据
*/ */
@Override @Override
public void setUser(User user) { public void setUser(IUser user) {
this.user = user; this.user = user;
} }

View File

@ -36,12 +36,12 @@ public interface Qo<T> {
* *
* @return 结果 * @return 结果
*/ */
User getUser(); IUser getUser();
/** /**
* 当前用户 * 当前用户
*/ */
void setUser(User user); void setUser(IUser user);
/** /**
* 获取结果集用于单例部署时快速封装 * 获取结果集用于单例部署时快速封装

View File

@ -5,7 +5,7 @@ import com.flyfish.framework.domain.po.User;
import com.flyfish.framework.enums.UserStatus; import com.flyfish.framework.enums.UserStatus;
import com.flyfish.framework.service.AuthenticationAuditor; import com.flyfish.framework.service.AuthenticationAuditor;
import com.flyfish.framework.service.AuthenticationLogger; import com.flyfish.framework.service.AuthenticationLogger;
import com.flyfish.framework.service.ReactiveUserService; import com.flyfish.framework.service.UserService;
import com.flyfish.framework.transform.ResultDataTransformer; import com.flyfish.framework.transform.ResultDataTransformer;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
@ -24,7 +24,7 @@ import java.util.Date;
public class AuthenticationAuditorImpl extends ResultDataTransformer implements AuthenticationAuditor { public class AuthenticationAuditorImpl extends ResultDataTransformer implements AuthenticationAuditor {
@Resource @Resource
private ReactiveUserService reactiveUserService; private UserService userService;
@Resource @Resource
private AuthenticationLogger authenticationLogger; private AuthenticationLogger authenticationLogger;
@ -40,7 +40,7 @@ public class AuthenticationAuditorImpl extends ResultDataTransformer implements
updating.setId(details.getId()); updating.setId(details.getId());
updating.setErrorCount(0); updating.setErrorCount(0);
updating.setLastTime(new Date()); updating.setLastTime(new Date());
return reactiveUserService.updateSelectiveById(updating).then(authenticationLogger.success(user)); return userService.updateSelectiveById(updating).then(authenticationLogger.success(user));
} }
/** /**
@ -56,7 +56,7 @@ public class AuthenticationAuditorImpl extends ResultDataTransformer implements
.getFormData() .getFormData()
.flatMap(data -> { .flatMap(data -> {
String username = data.getFirst("username"); String username = data.getFirst("username");
Mono<User> userMono = reactiveUserService.findByUsername(username).defaultIfEmpty(emptyUser(username)); Mono<User> userMono = userService.findByUsername(username).defaultIfEmpty(emptyUser(username));
// 当且仅当为凭据错误尝试 // 当且仅当为凭据错误尝试
if (exception instanceof BadCredentialsException) { if (exception instanceof BadCredentialsException) {
userMono = userMono.flatMap(user -> { userMono = userMono.flatMap(user -> {
@ -66,7 +66,7 @@ public class AuthenticationAuditorImpl extends ResultDataTransformer implements
if (updating.getErrorCount() >= 5) { if (updating.getErrorCount() >= 5) {
updating.setStatus(UserStatus.LOCKED); updating.setStatus(UserStatus.LOCKED);
} }
return reactiveUserService.updateSelectiveById(updating); return userService.updateSelectiveById(updating);
}); });
} }
return userMono.flatMap(user -> authenticationLogger.failure(user, exception)); return userMono.flatMap(user -> authenticationLogger.failure(user, exception));

View File

@ -14,7 +14,7 @@ import com.flyfish.framework.handler.JsonLogoutSuccessHandler;
import com.flyfish.framework.initializer.UserInitializer; import com.flyfish.framework.initializer.UserInitializer;
import com.flyfish.framework.service.AuthenticationAuditor; import com.flyfish.framework.service.AuthenticationAuditor;
import com.flyfish.framework.service.AuthenticationLogger; import com.flyfish.framework.service.AuthenticationLogger;
import com.flyfish.framework.service.ReactiveUserService; import com.flyfish.framework.service.UserService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -166,7 +166,7 @@ public class WebSecurityConfig {
@Bean @Bean
@ConditionalOnMissingBean(UserInitializer.class) @ConditionalOnMissingBean(UserInitializer.class)
@SuppressWarnings("all") @SuppressWarnings("all")
public UserInitializer userInitializer(ReactiveUserService userService) { public UserInitializer userInitializer(UserService userService) {
return () -> { return () -> {
UserQo qo = new UserQo(); UserQo qo = new UserQo();
qo.setType(UserType.SUPER_ADMIN.name()); qo.setType(UserType.SUPER_ADMIN.name());

View File

@ -1,6 +1,5 @@
package com.flyfish.framework.config.audit; package com.flyfish.framework.config.audit;
import com.flyfish.framework.domain.base.Domain;
import com.flyfish.framework.utils.UserUtils; import com.flyfish.framework.utils.UserUtils;
import org.springframework.data.domain.ReactiveAuditorAware; import org.springframework.data.domain.ReactiveAuditorAware;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
@ -25,8 +24,7 @@ public class ReactiveUserAuditor implements ReactiveAuditorAware<String> {
@NonNull @NonNull
public Mono<String> getCurrentAuditor() { public Mono<String> getCurrentAuditor() {
return ReactiveSecurityContextHolder.getContext() return ReactiveSecurityContextHolder.getContext()
.map(UserUtils::extractUser) .map(UserUtils::extractUserName)
.map(Domain::getName)
.defaultIfEmpty("匿名用户"); .defaultIfEmpty("匿名用户");
} }

View File

@ -5,6 +5,7 @@ import com.flyfish.framework.auditor.ReactiveBeanPoster;
import com.flyfish.framework.config.constants.UserCacheKeys; import com.flyfish.framework.config.constants.UserCacheKeys;
import com.flyfish.framework.domain.po.User; import com.flyfish.framework.domain.po.User;
import com.flyfish.framework.enums.UserType; import com.flyfish.framework.enums.UserType;
import com.flyfish.framework.service.UserDetailsConverter;
import com.flyfish.framework.utils.Assert; import com.flyfish.framework.utils.Assert;
import com.flyfish.framework.utils.ReactiveRedisOperations; import com.flyfish.framework.utils.ReactiveRedisOperations;
import com.flyfish.framework.utils.StrengthUtils; import com.flyfish.framework.utils.StrengthUtils;
@ -20,6 +21,7 @@ public class UserBeanAuditor implements ReactiveBeanAuditor<User>, ReactiveBeanP
private final PasswordEncoder passwordEncoder; private final PasswordEncoder passwordEncoder;
private final ReactiveRedisOperations reactiveRedisOperations; private final ReactiveRedisOperations reactiveRedisOperations;
private final UserDetailsConverter userDetailsConverter;
/** /**
* 对实体进行审查并补全相关字段 * 对实体进行审查并补全相关字段
@ -55,6 +57,7 @@ public class UserBeanAuditor implements ReactiveBeanAuditor<User>, ReactiveBeanP
@Override @Override
public Mono<User> post(User data) { public Mono<User> post(User data) {
// 更新缓存 // 更新缓存
return reactiveRedisOperations.del(UserCacheKeys.get(data.getUsername())).then(Mono.just(data)); return reactiveRedisOperations.set(UserCacheKeys.get(data.getUsername()),
userDetailsConverter.mapToUserDetails(data)).then(Mono.just(data));
} }
} }

View File

@ -10,9 +10,10 @@ import com.flyfish.framework.domain.base.IUser;
import com.flyfish.framework.domain.po.Role; import com.flyfish.framework.domain.po.Role;
import com.flyfish.framework.domain.po.User; import com.flyfish.framework.domain.po.User;
import com.flyfish.framework.enums.UserStatus; import com.flyfish.framework.enums.UserStatus;
import com.flyfish.framework.service.ReactiveUserService; import com.flyfish.framework.service.UserService;
import com.flyfish.framework.utils.Assert; import com.flyfish.framework.utils.Assert;
import com.flyfish.framework.utils.StrengthUtils; import com.flyfish.framework.utils.StrengthUtils;
import com.flyfish.framework.utils.UserUtils;
import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -84,21 +85,20 @@ public class UserController extends ReactiveBaseController<User, UserQo> {
} }
/** /**
* 更新用户状态主要更新部门 * 更新用户状态主要更新部门同时更新缓存
* *
* @param authorize 鉴权 * @param authorize 鉴权
* @return 结果 * @return 结果
*/ */
@PatchMapping("/status") @PatchMapping("/status")
public Mono<Result<User>> updateStatus(String authorize) { public Mono<Result<User>> updateStatus(String authorize) {
ReactiveUserService reactiveService = (ReactiveUserService) this.reactiveService; UserService reactiveService = (UserService) this.reactiveService;
return ReactiveSecurityContextHolder.getContext() return ReactiveSecurityContextHolder.getContext()
.map(context -> (IUser) context.getAuthentication().getPrincipal()) .map(UserUtils::extractUserDetails)
.flatMap(user -> { .flatMap(user -> {
user.setAuthority(authorize);
User updating = new User(); User updating = new User();
updating.setId(user.getId()); updating.setId(user.getId());
updating.setAuthority(user.getAuthority()); updating.setAuthority(authorize);
return reactiveService.updateSelectiveById(updating); return reactiveService.updateSelectiveById(updating);
}) })
.map(Result::ok); .map(Result::ok);

View File

@ -0,0 +1,125 @@
package com.flyfish.framework.controller.authorized;
import com.flyfish.framework.annotations.Operation;
import com.flyfish.framework.bean.Result;
import com.flyfish.framework.bean.SyncVo;
import com.flyfish.framework.configuration.annotations.CurrentUser;
import com.flyfish.framework.configuration.annotations.PagedQuery;
import com.flyfish.framework.configuration.annotations.ValidRequestBody;
import com.flyfish.framework.constant.ReactiveConstants;
import com.flyfish.framework.context.UserContext;
import com.flyfish.framework.controller.SafeController;
import com.flyfish.framework.domain.authorized.AuthorizedDomain;
import com.flyfish.framework.domain.authorized.AuthorizedQo;
import com.flyfish.framework.domain.po.User;
import com.flyfish.framework.service.BaseReactiveService;
import com.flyfish.framework.service.BaseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import java.util.List;
/**
* 通用RESTful的控制器用于动态提供基础的RESTful接口
*
* @author wangyu
* @create 2017-06-15 8:48
*/
public abstract class AuthorizedController<T extends AuthorizedDomain, Q extends AuthorizedQo<T>> implements SafeController {
@Autowired
protected BaseService<T> service;
@Autowired(required = false)
protected BaseReactiveService<T> reactiveService;
@Autowired
protected UserContext userContext;
@SuppressWarnings("unchecked")
public <S extends BaseService<T>> S getService() {
return (S) service;
}
@GetMapping("/exists")
public Result<Boolean> exists(@PagedQuery Q qo) {
return Result.accept(service.count(qo) != 0);
}
@GetMapping("/count")
public Result<Long> count(@PagedQuery Q qo) {
return Result.accept(service.count(qo));
}
@PostMapping("")
@Operation.Create
public Result<T> create(@ValidRequestBody T entity, @CurrentUser User user) {
userContext.setUser(user);
return Result.accept(service.create(entity));
}
@GetMapping("{id}")
public Result<T> get(@PathVariable String id) {
return service.getById(id).map(Result::accept).orElse(Result.notFound());
}
@PutMapping("{id}")
@Operation.Update
public Result<T> update(@ValidRequestBody T entity, @CurrentUser User user) {
userContext.setUser(user);
return Result.accept(service.updateSelectiveById(entity));
}
@PatchMapping("{id}")
@Operation.Update
public Result<T> patch(@RequestBody T entity, @CurrentUser User user) {
userContext.setUser(user);
return Result.accept(service.updateSelectiveById(entity));
}
@PutMapping("")
@Operation.UpdateAll
public Result<List<T>> updateList(@RequestBody List<T> list, @CurrentUser User user) {
userContext.setUser(user);
return Result.accept(service.updateBatch(list));
}
@PutMapping("/sync")
@Operation.Sync
public Result<SyncVo<T>> syncList(@RequestBody List<T> list, @CurrentUser User user) {
userContext.setUser(user);
return Result.accept(service.sync(list));
}
@DeleteMapping("{id}")
@Operation.Delete
public Result<T> remove(@PathVariable List<String> id, @CurrentUser User user) {
userContext.setUser(user);
service.deleteBatchByIds(id);
return Result.ok();
}
@GetMapping("/all")
public Result<List<T>> all(@PagedQuery Q qo) {
if (qo.isEmpty()) {
return Result.accept(service.getAll());
}
return Result.accept(service.getList(qo));
}
@GetMapping("")
public Result<List<T>> list(@PagedQuery Q qo) {
if (null != qo.getPageable()) {
return Result.accept(service.getPageList(qo));
}
return Result.accept(service.getList(qo));
}
@PutMapping(value = "{id}", headers = ReactiveConstants.USE_REACTIVE)
public Mono<Result<T>> reactiveUpdate(@ValidRequestBody T entity) {
return reactiveService.updateById(entity)
.map(Result::accept)
.defaultIfEmpty(Result.notFound());
}
}

View File

@ -0,0 +1,100 @@
package com.flyfish.framework.controller.authorized;
import com.flyfish.framework.annotations.Operation;
import com.flyfish.framework.bean.Result;
import com.flyfish.framework.bean.SyncVo;
import com.flyfish.framework.configuration.annotations.PagedQuery;
import com.flyfish.framework.configuration.annotations.ValidRequestBody;
import com.flyfish.framework.controller.SafeController;
import com.flyfish.framework.domain.authorized.AuthorizedDomain;
import com.flyfish.framework.domain.authorized.AuthorizedQo;
import com.flyfish.framework.service.BaseReactiveService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.util.CastUtils;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import java.util.List;
/**
* 多租户带授权的控制器
*
* @param <T> 实体泛型
* @param <Q> 查询实体泛型
*/
public abstract class ReactiveAuthorizedController<T extends AuthorizedDomain, Q extends AuthorizedQo<T>> implements SafeController {
@Autowired
protected BaseReactiveService<T> reactiveService;
public <S extends BaseReactiveService<T>> S getService() {
return CastUtils.cast(reactiveService);
}
@GetMapping("/exists")
public Mono<Result<Boolean>> exists(@PagedQuery Q qo) {
return reactiveService.exists(qo).map(Result::accept);
}
@GetMapping("/count")
public Mono<Result<Long>> count(@PagedQuery Q qo) {
return reactiveService.count(qo).map(Result::accept);
}
@PostMapping("")
@Operation.Create
public Mono<Result<T>> create(@ValidRequestBody T entity) {
return reactiveService.create(entity).map(Result::accept);
}
@GetMapping("{id}")
public Mono<Result<T>> get(@PathVariable String id) {
return reactiveService.getById(id).map(Result::accept).defaultIfEmpty(Result.notFound());
}
@PutMapping("{id}")
@Operation.Update
public Mono<Result<T>> update(@ValidRequestBody T entity) {
return reactiveService.updateSelectiveById(entity).map(Result::accept).defaultIfEmpty(Result.notFound());
}
@PatchMapping("{id}")
@Operation.Update
public Mono<Result<T>> patch(@RequestBody T entity) {
return reactiveService.updateSelectiveById(entity).map(Result::accept).defaultIfEmpty(Result.notFound());
}
@PutMapping("")
@Operation.UpdateAll
public Mono<Result<List<T>>> updateList(@RequestBody List<T> list) {
return reactiveService.updateBatch(list).collectList().map(Result::accept).defaultIfEmpty(Result.emptyList());
}
@PutMapping("/sync")
@Operation.Sync
public Mono<Result<SyncVo<T>>> syncList(@RequestBody List<T> list) {
return reactiveService.sync(list).map(Result::accept).defaultIfEmpty(Result.accept(SyncVo.<T>builder().build()));
}
@DeleteMapping("{id}")
@Operation.Delete
public Mono<Result<T>> remove(@PathVariable List<String> id) {
return reactiveService.deleteBatchByIds(id).thenReturn(Result.ok());
}
@GetMapping("/all")
public Mono<Result<List<T>>> all(@PagedQuery Q qo) {
if (qo.isEmpty()) {
return reactiveService.getAll().collectList().map(Result::accept).defaultIfEmpty(Result.emptyList());
}
return reactiveService.getList(qo).collectList().map(Result::accept).defaultIfEmpty(Result.emptyList());
}
@GetMapping("")
public Mono<Result<List<T>>> list(@PagedQuery Q qo) {
if (null != qo.getPageable() && qo.getPageable().isPaged()) {
return reactiveService.getPageList(qo).map(Result::accept).defaultIfEmpty(Result.emptyPage());
}
return reactiveService.getList(qo).collectList().map(Result::accept).defaultIfEmpty(Result.emptyList());
}
}

View File

@ -2,6 +2,7 @@ package com.flyfish.framework.domain;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.flyfish.framework.domain.authorized.AuthorizedUserDetails;
import com.flyfish.framework.domain.base.Domain; import com.flyfish.framework.domain.base.Domain;
import com.flyfish.framework.domain.base.IUser; import com.flyfish.framework.domain.base.IUser;
import com.flyfish.framework.domain.po.Department; import com.flyfish.framework.domain.po.Department;
@ -30,7 +31,7 @@ import java.util.List;
*/ */
@Getter @Getter
@Setter @Setter
public class AdminUserDetails implements UserDetails, IUser { public class AdminUserDetails implements UserDetails, IUser, AuthorizedUserDetails {
private static final long serialVersionUID = -2441854985340378429L; private static final long serialVersionUID = -2441854985340378429L;
@ -101,6 +102,10 @@ public class AdminUserDetails implements UserDetails, IUser {
* 查询冗余标记用户信息 * 查询冗余标记用户信息
*/ */
private Object detail; private Object detail;
/**
* 权限code列表
*/
private List<String> authorityCodes;
/** /**
* 判断是否是管理员 * 判断是否是管理员

View File

@ -1,64 +0,0 @@
package com.flyfish.framework.domain.authorized;
import com.flyfish.framework.builder.CriteriaBuilder;
import com.flyfish.framework.context.SpringContext;
import com.flyfish.framework.domain.base.NameLikeQo;
import com.flyfish.framework.domain.po.Department;
import com.flyfish.framework.enums.UserType;
import com.flyfish.framework.service.DepartmentService;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import org.apache.commons.collections4.CollectionUtils;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* 带鉴权的查询实体主要以部门隔绝
*
* @param <T>
*/
@Getter
@Setter
public abstract class AuthorizedQo<T extends AuthorizedDomain> extends NameLikeQo<T> {
// 部门服务
private DepartmentService departmentService;
/**
* 获取可见的权限ids
*
* @return 结果
*/
public List<String> getAuthorizedIds() {
if (null != user) {
// 是超级管理员放行返回null
if (user.getType() == UserType.SUPER_ADMIN) {
return null;
}
if (CollectionUtils.isNotEmpty(user.getDepartments())) {
val departs = user.getDepartments().stream().map(Department::getId)
.collect(Collectors.toList());
val result = getSubAuthorities(departs);
if (CollectionUtils.isNotEmpty(result)) {
return result;
}
}
}
return Collections.singletonList(Department.PUBLIC);
}
@Override
public CriteriaBuilder<T> criteriaBuilder() {
return super.criteriaBuilder().with("authorizedIds", "authorizeId", CriteriaBuilder.Builders.IN);
}
private List<String> getSubAuthorities(List<String> parents) {
if (null == departmentService) {
departmentService = SpringContext.getBean(DepartmentService.class);
}
return departmentService.getSubCodes(parents);
}
}

View File

@ -2,14 +2,14 @@ package com.flyfish.framework.service;
import com.flyfish.framework.domain.po.Department; import com.flyfish.framework.domain.po.Department;
import com.flyfish.framework.service.impl.BaseReactiveServiceImpl; import com.flyfish.framework.service.impl.BaseReactiveServiceImpl;
import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
/** /**
* 部门服务 * 部门服务
@ -20,17 +20,16 @@ import java.util.stream.Collectors;
public class DepartmentService extends BaseReactiveServiceImpl<Department> { public class DepartmentService extends BaseReactiveServiceImpl<Department> {
@Resource @Resource
private MongoOperations mongoOperations; private ReactiveMongoOperations reactiveMongoOperations;
/** /**
* 获取子部门列表 * 获取子部门列表
* *
* @return 结果 * @return 结果
*/ */
public List<String> getSubCodes(List<String> parents) { public Mono<List<String>> getSubCodes(List<String> parents) {
Query query = Query.query(Criteria.where("parentId").in(parents)); Query query = Query.query(Criteria.where("parentId").in(parents));
query.fields().include("_id"); query.fields().include("_id");
return mongoOperations.find(query, Department.class).stream().map(Department::getId) return reactiveMongoOperations.find(query, Department.class).map(Department::getId).collectList();
.collect(Collectors.toList());
} }
} }

View File

@ -1,12 +1,10 @@
package com.flyfish.framework.service; package com.flyfish.framework.service;
import com.flyfish.framework.config.constants.UserCacheKeys; import com.flyfish.framework.config.constants.UserCacheKeys;
import com.flyfish.framework.domain.AdminUserDetails;
import com.flyfish.framework.domain.base.IUser; import com.flyfish.framework.domain.base.IUser;
import com.flyfish.framework.domain.po.User; import com.flyfish.framework.domain.po.User;
import com.flyfish.framework.enums.UserStatus; import com.flyfish.framework.enums.UserStatus;
import com.flyfish.framework.utils.Assert; import com.flyfish.framework.utils.Assert;
import com.flyfish.framework.utils.CopyUtils;
import com.flyfish.framework.utils.ReactiveRedisOperations; import com.flyfish.framework.utils.ReactiveRedisOperations;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
@ -52,7 +50,8 @@ public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
() -> new LockedException("账户已经锁定!请联系管理员修改密码!")); () -> new LockedException("账户已经锁定!请联系管理员修改密码!"));
} }
private final ReactiveUserService userService; private final UserService userService;
private final UserDetailsConverter userDetailsConverter;
@Resource @Resource
private ServerSecurityContextRepository contextRepository; private ServerSecurityContextRepository contextRepository;
@ -72,27 +71,23 @@ public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
} }
@Override @Override
public Mono<UserDetails> updatePassword(UserDetails userDetails, String s) { public Mono<UserDetails> updatePassword(UserDetails userDetails, String password) {
return userService.updateById(((IUser) userDetails).toUser()).map(this::mapToUserDetails); return userService.updateById(((IUser) userDetails).toUser()).thenReturn(userDetails);
} }
@Override @Override
public Mono<UserDetails> findByUsername(String s) { public Mono<UserDetails> findByUsername(String username) {
String key = UserCacheKeys.get(s); String key = UserCacheKeys.get(username);
// 优先从缓存读取如果缓存不存在查询转换后放入缓存 // 优先从缓存读取如果缓存不存在查询转换后放入缓存
return reactiveRedisOperations.hasKey(key).flatMap(exists -> Boolean.TRUE.equals(exists) ? reactiveRedisOperations.get(key) : return reactiveRedisOperations.hasKey(key).flatMap(exists -> Boolean.TRUE.equals(exists) ? reactiveRedisOperations.get(key) :
userService.findByUsername(s).flatMap(this::validate).map(this::mapToUserDetails) userService.findByUsername(username)
.flatMap(this::validate)
.flatMap(userDetailsConverter::mapToUserDetails)
.flatMap(detail -> reactiveRedisOperations.set(key, detail).thenReturn(detail)) .flatMap(detail -> reactiveRedisOperations.set(key, detail).thenReturn(detail))
) )
.switchIfEmpty(Mono.defer(() -> Mono.error(new UsernameNotFoundException("用户不存在!")))); .switchIfEmpty(Mono.defer(() -> Mono.error(new UsernameNotFoundException("用户不存在!"))));
} }
private UserDetails mapToUserDetails(IUser user) {
AdminUserDetails userDetail = new AdminUserDetails();
CopyUtils.copyProps(user, userDetail);
return userDetail;
}
/** /**
* 校验 * 校验
* *
@ -117,7 +112,7 @@ public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
public Mono<Authentication> authenticate(UserDetails user, ServerWebExchange exchange) { public Mono<Authentication> authenticate(UserDetails user, ServerWebExchange exchange) {
return loadContext(user) return loadContext(user)
.flatMap(securityContext -> contextRepository.save(exchange, securityContext) .flatMap(securityContext -> contextRepository.save(exchange, securityContext)
.subscriberContext(ReactiveSecurityContextHolder.withSecurityContext( .contextWrite(ReactiveSecurityContextHolder.withSecurityContext(
Mono.just(securityContext))) Mono.just(securityContext)))
.then(Mono.just(securityContext))) .then(Mono.just(securityContext)))
.map(SecurityContext::getAuthentication) .map(SecurityContext::getAuthentication)
@ -156,4 +151,5 @@ public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
.flatMap(context -> contextRepository.save(exchange, context)); .flatMap(context -> contextRepository.save(exchange, context));
} }
} }

View File

@ -1,28 +0,0 @@
package com.flyfish.framework.service;
import com.flyfish.framework.domain.po.User;
import com.flyfish.framework.repository.ReactiveUserRepository;
import com.flyfish.framework.service.impl.BaseReactiveServiceImpl;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
/**
* 异步用户service
*
* @author wangyu
*/
@Service
public class ReactiveUserService extends BaseReactiveServiceImpl<User> implements UserFindService {
/**
* 获取用户数据
*
* @param username 用户
* @return 结果
*/
@Override
public Mono<User> findByUsername(String username) {
return ((ReactiveUserRepository) repository).findByUsername(username);
}
}

View File

@ -0,0 +1,51 @@
package com.flyfish.framework.service;
import com.flyfish.framework.domain.AdminUserDetails;
import com.flyfish.framework.domain.base.IUser;
import com.flyfish.framework.domain.po.Department;
import com.flyfish.framework.enums.UserType;
import com.flyfish.framework.utils.CopyUtils;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import java.util.Collections;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class UserDetailsConverter {
private final DepartmentService departmentService;
/**
* 将用户映射为用户详情查询所有权限code
*
* @param user 用户信息
* @return 结果
*/
public Mono<UserDetails> mapToUserDetails(IUser user) {
AdminUserDetails userDetail = new AdminUserDetails();
CopyUtils.copyProps(user, userDetail);
userDetail.setAuthorityCodes(Collections.singletonList(Department.PUBLIC));
if (user.getType() == UserType.SUPER_ADMIN) {
userDetail.setAuthorityCodes(null);
}
if (CollectionUtils.isNotEmpty(user.getDepartments())) {
val departs = user.getDepartments().stream().map(Department::getId)
.collect(Collectors.toList());
return departmentService.getSubCodes(departs)
.map(codes -> {
if (CollectionUtils.isNotEmpty(codes)) {
userDetail.setAuthorityCodes(ListUtils.union(codes, userDetail.getAuthorityCodes()));
}
return userDetail;
});
}
return Mono.just(userDetail);
}
}

View File

@ -1,10 +1,28 @@
package com.flyfish.framework.service; package com.flyfish.framework.service;
import com.flyfish.framework.domain.po.User; import com.flyfish.framework.domain.po.User;
import com.flyfish.framework.service.impl.BaseServiceImpl; import com.flyfish.framework.repository.ReactiveUserRepository;
import com.flyfish.framework.service.impl.BaseReactiveServiceImpl;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
/**
* 异步用户service
*
* @author wangyu
*/
@Service @Service
public class UserService extends BaseServiceImpl<User> { public class UserService extends BaseReactiveServiceImpl<User> implements UserFindService {
/**
* 获取用户数据
*
* @param username 用户
* @return 结果
*/
@Override
public Mono<User> findByUsername(String username) {
return ((ReactiveUserRepository) repository).findByUsername(username);
}
} }

View File

@ -13,4 +13,5 @@ import java.lang.annotation.Target;
@Target(ElementType.PARAMETER) @Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface PagedQuery { public @interface PagedQuery {
} }

View File

@ -6,6 +6,7 @@ import com.flyfish.framework.utils.UserUtils;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.util.CastUtils;
import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.BindingContext;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport;
@ -29,6 +30,7 @@ public class PageQueryArgumentResolver extends HandlerMethodArgumentResolverSupp
*/ */
private ModelAttributeFilter modelAttributeFilter; private ModelAttributeFilter modelAttributeFilter;
public PageQueryArgumentResolver(ReactiveAdapterRegistry adapterRegistry) { public PageQueryArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
super(adapterRegistry); super(adapterRegistry);
this.pageableParameterFilter = new PageParameterFilter(); this.pageableParameterFilter = new PageParameterFilter();
@ -56,14 +58,14 @@ public class PageQueryArgumentResolver extends HandlerMethodArgumentResolverSupp
// 使用spring的默认解析器解析所有变量 // 使用spring的默认解析器解析所有变量
return modelAttributeFilter.filter(parameter, bindingContext, exchange).flatMap(qo -> { return modelAttributeFilter.filter(parameter, bindingContext, exchange).flatMap(qo -> {
if (qo instanceof Qo) { if (qo instanceof Qo) {
Qo query = (Qo) qo; Qo<?> query = (Qo<?>) qo;
query.setPageable(pageable); query.setPageable(pageable);
return ReactiveSecurityContextHolder.getContext() return ReactiveSecurityContextHolder.getContext()
.map(UserUtils::extractUser) .map(UserUtils::extractUserDetails)
.map(user -> { .map(user -> {
query.setUser(user); query.setUser(user);
return query; return query;
}).defaultIfEmpty(query); }).defaultIfEmpty(CastUtils.cast(query));
} }
return Mono.just(qo); return Mono.just(qo);
}); });

View File

@ -3,6 +3,7 @@ package com.flyfish.framework.service.impl;
import com.flyfish.framework.auditor.ReactiveBeanAuditor; import com.flyfish.framework.auditor.ReactiveBeanAuditor;
import com.flyfish.framework.auditor.ReactiveBeanPoster; import com.flyfish.framework.auditor.ReactiveBeanPoster;
import com.flyfish.framework.bean.SyncVo; import com.flyfish.framework.bean.SyncVo;
import com.flyfish.framework.domain.authorized.AuthorizedDomain;
import com.flyfish.framework.domain.base.AuditDomain; import com.flyfish.framework.domain.base.AuditDomain;
import com.flyfish.framework.domain.base.Domain; import com.flyfish.framework.domain.base.Domain;
import com.flyfish.framework.domain.base.Qo; import com.flyfish.framework.domain.base.Qo;
@ -42,6 +43,8 @@ public class BaseReactiveServiceImpl<T extends Domain> implements BaseReactiveSe
protected ReactiveBeanPoster<T> poster; protected ReactiveBeanPoster<T> poster;
@Autowired @Autowired
private ReactiveBeanAuditor<AuditDomain> operationAuditor; private ReactiveBeanAuditor<AuditDomain> operationAuditor;
@Autowired
private ReactiveBeanAuditor<AuthorizedDomain> authorizeAuditor;
/** /**
* 查询 * 查询
@ -369,15 +372,20 @@ public class BaseReactiveServiceImpl<T extends Domain> implements BaseReactiveSe
* @param entity 实体 * @param entity 实体
*/ */
protected Mono<T> audit(T entity) { protected Mono<T> audit(T entity) {
// 用户审查 Mono<T> mono = Mono.just(entity);
if (entity instanceof AuditDomain) { // 首先自定义审查可能返回自定义对象
return CastUtils.cast(operationAuditor.audit((AuditDomain) entity));
}
// 自定义审查
if (auditor != null) { if (auditor != null) {
return auditor.audit(entity); mono = mono.flatMap(auditor::audit);
} }
return Mono.just(entity); // 操作信息审查
if (entity instanceof AuditDomain) {
mono = CastUtils.cast(mono.cast(AuditDomain.class).flatMap(operationAuditor::audit));
}
// 权限审查
if (entity instanceof AuthorizedDomain) {
mono = CastUtils.cast(mono.cast(AuthorizedDomain.class).flatMap(authorizeAuditor::audit));
}
return mono;
} }
/** /**

View File

@ -3,6 +3,7 @@ package com.flyfish.framework.service.impl;
import com.flyfish.framework.auditor.BeanAuditor; import com.flyfish.framework.auditor.BeanAuditor;
import com.flyfish.framework.auditor.BeanPoster; import com.flyfish.framework.auditor.BeanPoster;
import com.flyfish.framework.bean.SyncVo; import com.flyfish.framework.bean.SyncVo;
import com.flyfish.framework.domain.authorized.AuthorizedDomain;
import com.flyfish.framework.domain.base.AuditDomain; import com.flyfish.framework.domain.base.AuditDomain;
import com.flyfish.framework.domain.base.Domain; import com.flyfish.framework.domain.base.Domain;
import com.flyfish.framework.domain.base.Qo; import com.flyfish.framework.domain.base.Qo;
@ -35,6 +36,8 @@ public class BaseServiceImpl<T extends Domain> implements BaseService<T> {
protected BeanPoster<T> poster; protected BeanPoster<T> poster;
@Autowired @Autowired
private BeanAuditor<AuditDomain> operationAuditor; private BeanAuditor<AuditDomain> operationAuditor;
@Autowired
private BeanAuditor<AuthorizedDomain> authorizeAuditor;
/** /**
* 查询 * 查询
@ -341,13 +344,17 @@ public class BaseServiceImpl<T extends Domain> implements BaseService<T> {
* @param entity 实体 * @param entity 实体
*/ */
protected void audit(T entity) { protected void audit(T entity) {
// 自定义审查
if (auditor != null) {
auditor.audit(entity);
}
// 用户审查 // 用户审查
if (entity instanceof AuditDomain) { if (entity instanceof AuditDomain) {
operationAuditor.audit((AuditDomain) entity); operationAuditor.audit((AuditDomain) entity);
} }
// 自定义审查 // 权限审查
if (auditor != null) { if (entity instanceof AuthorizedDomain) {
auditor.audit(entity); authorizeAuditor.audit((AuthorizedDomain) entity);
} }
} }

View File

@ -22,6 +22,26 @@ public class UserUtils {
return ((IUser) securityContext.getAuthentication().getPrincipal()).toUser(); return ((IUser) securityContext.getAuthentication().getPrincipal()).toUser();
} }
/**
* 解压用户详情
*
* @param securityContext 上下文
* @return 结果
*/
public static IUser extractUserDetails(SecurityContext securityContext) {
return (IUser) securityContext.getAuthentication().getPrincipal();
}
/**
* 解压用户名称
*
* @param securityContext 上下文
* @return 结果
*/
public static String extractUserName(SecurityContext securityContext) {
return ((IUser) securityContext.getAuthentication().getPrincipal()).getName();
}
/** /**
* 转换用户 * 转换用户
* *