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

View File

@ -36,12 +36,12 @@ public interface Qo<T> {
*
* @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.service.AuthenticationAuditor;
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 org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
@ -24,7 +24,7 @@ import java.util.Date;
public class AuthenticationAuditorImpl extends ResultDataTransformer implements AuthenticationAuditor {
@Resource
private ReactiveUserService reactiveUserService;
private UserService userService;
@Resource
private AuthenticationLogger authenticationLogger;
@ -40,7 +40,7 @@ public class AuthenticationAuditorImpl extends ResultDataTransformer implements
updating.setId(details.getId());
updating.setErrorCount(0);
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()
.flatMap(data -> {
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) {
userMono = userMono.flatMap(user -> {
@ -66,7 +66,7 @@ public class AuthenticationAuditorImpl extends ResultDataTransformer implements
if (updating.getErrorCount() >= 5) {
updating.setStatus(UserStatus.LOCKED);
}
return reactiveUserService.updateSelectiveById(updating);
return userService.updateSelectiveById(updating);
});
}
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.service.AuthenticationAuditor;
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.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -166,7 +166,7 @@ public class WebSecurityConfig {
@Bean
@ConditionalOnMissingBean(UserInitializer.class)
@SuppressWarnings("all")
public UserInitializer userInitializer(ReactiveUserService userService) {
public UserInitializer userInitializer(UserService userService) {
return () -> {
UserQo qo = new UserQo();
qo.setType(UserType.SUPER_ADMIN.name());

View File

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

View File

@ -5,6 +5,7 @@ import com.flyfish.framework.auditor.ReactiveBeanPoster;
import com.flyfish.framework.config.constants.UserCacheKeys;
import com.flyfish.framework.domain.po.User;
import com.flyfish.framework.enums.UserType;
import com.flyfish.framework.service.UserDetailsConverter;
import com.flyfish.framework.utils.Assert;
import com.flyfish.framework.utils.ReactiveRedisOperations;
import com.flyfish.framework.utils.StrengthUtils;
@ -20,6 +21,7 @@ public class UserBeanAuditor implements ReactiveBeanAuditor<User>, ReactiveBeanP
private final PasswordEncoder passwordEncoder;
private final ReactiveRedisOperations reactiveRedisOperations;
private final UserDetailsConverter userDetailsConverter;
/**
* 对实体进行审查并补全相关字段
@ -55,6 +57,7 @@ public class UserBeanAuditor implements ReactiveBeanAuditor<User>, ReactiveBeanP
@Override
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.User;
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.StrengthUtils;
import com.flyfish.framework.utils.UserUtils;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
@ -84,21 +85,20 @@ public class UserController extends ReactiveBaseController<User, UserQo> {
}
/**
* 更新用户状态主要更新部门
* 更新用户状态主要更新部门同时更新缓存
*
* @param authorize 鉴权
* @return 结果
*/
@PatchMapping("/status")
public Mono<Result<User>> updateStatus(String authorize) {
ReactiveUserService reactiveService = (ReactiveUserService) this.reactiveService;
UserService reactiveService = (UserService) this.reactiveService;
return ReactiveSecurityContextHolder.getContext()
.map(context -> (IUser) context.getAuthentication().getPrincipal())
.map(UserUtils::extractUserDetails)
.flatMap(user -> {
user.setAuthority(authorize);
User updating = new User();
updating.setId(user.getId());
updating.setAuthority(user.getAuthority());
updating.setAuthority(authorize);
return reactiveService.updateSelectiveById(updating);
})
.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.JsonIgnore;
import com.flyfish.framework.domain.authorized.AuthorizedUserDetails;
import com.flyfish.framework.domain.base.Domain;
import com.flyfish.framework.domain.base.IUser;
import com.flyfish.framework.domain.po.Department;
@ -30,7 +31,7 @@ import java.util.List;
*/
@Getter
@Setter
public class AdminUserDetails implements UserDetails, IUser {
public class AdminUserDetails implements UserDetails, IUser, AuthorizedUserDetails {
private static final long serialVersionUID = -2441854985340378429L;
@ -101,6 +102,10 @@ public class AdminUserDetails implements UserDetails, IUser {
* 查询冗余标记用户信息
*/
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.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.Query;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;
/**
* 部门服务
@ -20,17 +20,16 @@ import java.util.stream.Collectors;
public class DepartmentService extends BaseReactiveServiceImpl<Department> {
@Resource
private MongoOperations mongoOperations;
private ReactiveMongoOperations reactiveMongoOperations;
/**
* 获取子部门列表
*
* @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.fields().include("_id");
return mongoOperations.find(query, Department.class).stream().map(Department::getId)
.collect(Collectors.toList());
return reactiveMongoOperations.find(query, Department.class).map(Department::getId).collectList();
}
}

View File

@ -1,12 +1,10 @@
package com.flyfish.framework.service;
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.po.User;
import com.flyfish.framework.enums.UserStatus;
import com.flyfish.framework.utils.Assert;
import com.flyfish.framework.utils.CopyUtils;
import com.flyfish.framework.utils.ReactiveRedisOperations;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.BooleanUtils;
@ -52,7 +50,8 @@ public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
() -> new LockedException("账户已经锁定!请联系管理员修改密码!"));
}
private final ReactiveUserService userService;
private final UserService userService;
private final UserDetailsConverter userDetailsConverter;
@Resource
private ServerSecurityContextRepository contextRepository;
@ -72,27 +71,23 @@ public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
}
@Override
public Mono<UserDetails> updatePassword(UserDetails userDetails, String s) {
return userService.updateById(((IUser) userDetails).toUser()).map(this::mapToUserDetails);
public Mono<UserDetails> updatePassword(UserDetails userDetails, String password) {
return userService.updateById(((IUser) userDetails).toUser()).thenReturn(userDetails);
}
@Override
public Mono<UserDetails> findByUsername(String s) {
String key = UserCacheKeys.get(s);
public Mono<UserDetails> findByUsername(String username) {
String key = UserCacheKeys.get(username);
// 优先从缓存读取如果缓存不存在查询转换后放入缓存
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))
)
.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) {
return loadContext(user)
.flatMap(securityContext -> contextRepository.save(exchange, securityContext)
.subscriberContext(ReactiveSecurityContextHolder.withSecurityContext(
.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(
Mono.just(securityContext)))
.then(Mono.just(securityContext)))
.map(SecurityContext::getAuthentication)
@ -156,4 +151,5 @@ public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
.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;
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 reactor.core.publisher.Mono;
/**
* 异步用户service
*
* @author wangyu
*/
@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)
@Retention(RetentionPolicy.RUNTIME)
public @interface PagedQuery {
}

View File

@ -6,6 +6,7 @@ import com.flyfish.framework.utils.UserUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.data.domain.Pageable;
import org.springframework.data.util.CastUtils;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.web.reactive.BindingContext;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport;
@ -29,6 +30,7 @@ public class PageQueryArgumentResolver extends HandlerMethodArgumentResolverSupp
*/
private ModelAttributeFilter modelAttributeFilter;
public PageQueryArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
super(adapterRegistry);
this.pageableParameterFilter = new PageParameterFilter();
@ -56,14 +58,14 @@ public class PageQueryArgumentResolver extends HandlerMethodArgumentResolverSupp
// 使用spring的默认解析器解析所有变量
return modelAttributeFilter.filter(parameter, bindingContext, exchange).flatMap(qo -> {
if (qo instanceof Qo) {
Qo query = (Qo) qo;
Qo<?> query = (Qo<?>) qo;
query.setPageable(pageable);
return ReactiveSecurityContextHolder.getContext()
.map(UserUtils::extractUser)
.map(UserUtils::extractUserDetails)
.map(user -> {
query.setUser(user);
return query;
}).defaultIfEmpty(query);
}).defaultIfEmpty(CastUtils.cast(query));
}
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.ReactiveBeanPoster;
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.Domain;
import com.flyfish.framework.domain.base.Qo;
@ -42,6 +43,8 @@ public class BaseReactiveServiceImpl<T extends Domain> implements BaseReactiveSe
protected ReactiveBeanPoster<T> poster;
@Autowired
private ReactiveBeanAuditor<AuditDomain> operationAuditor;
@Autowired
private ReactiveBeanAuditor<AuthorizedDomain> authorizeAuditor;
/**
* 查询
@ -369,15 +372,20 @@ public class BaseReactiveServiceImpl<T extends Domain> implements BaseReactiveSe
* @param entity 实体
*/
protected Mono<T> audit(T entity) {
// 用户审查
if (entity instanceof AuditDomain) {
return CastUtils.cast(operationAuditor.audit((AuditDomain) entity));
}
// 自定义审查
Mono<T> mono = Mono.just(entity);
// 首先自定义审查可能返回自定义对象
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.BeanPoster;
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.Domain;
import com.flyfish.framework.domain.base.Qo;
@ -35,6 +36,8 @@ public class BaseServiceImpl<T extends Domain> implements BaseService<T> {
protected BeanPoster<T> poster;
@Autowired
private BeanAuditor<AuditDomain> operationAuditor;
@Autowired
private BeanAuditor<AuthorizedDomain> authorizeAuditor;
/**
* 查询
@ -341,13 +344,17 @@ public class BaseServiceImpl<T extends Domain> implements BaseService<T> {
* @param entity 实体
*/
protected void audit(T entity) {
// 自定义审查
if (auditor != null) {
auditor.audit(entity);
}
// 用户审查
if (entity instanceof AuditDomain) {
operationAuditor.audit((AuditDomain) entity);
}
// 自定义审查
if (auditor != null) {
auditor.audit(entity);
// 权限审查
if (entity instanceof AuthorizedDomain) {
authorizeAuditor.audit((AuthorizedDomain) entity);
}
}

View File

@ -22,6 +22,26 @@ public class UserUtils {
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();
}
/**
* 转换用户
*