fix: 密码机制

This commit is contained in:
wangyu 2021-01-13 00:43:21 +08:00
parent 88f97a6896
commit 8c3048316a
17 changed files with 124 additions and 46 deletions

View File

@ -5,9 +5,9 @@ import lombok.Getter;
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum RoleType { public enum RoleType implements NamedEnum {
PC("PC端"), MOBILE("移动端"); PC("PC端"), MOBILE("移动端");
private String name; private final String name;
} }

View File

@ -11,9 +11,9 @@ import lombok.Getter;
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum UserStatus { public enum UserStatus implements NamedEnum {
NORMAL("正常状态"), LOCKED("已锁定"), DISABLED("已禁用"), EXPIRED("已过期"); NORMAL("正常状态"), LOCKED("已锁定"), DISABLED("已禁用"), EXPIRED("已过期");
private String name; private final String name;
} }

View File

@ -13,7 +13,7 @@ import java.util.Map;
*/ */
@AllArgsConstructor @AllArgsConstructor
@Getter @Getter
public enum UserType { public enum UserType implements NamedEnum {
SUPER_ADMIN("超级管理员"), ADMIN("管理员"), VIP("会员"), USER("非会员"); SUPER_ADMIN("超级管理员"), ADMIN("管理员"), VIP("会员"), USER("非会员");
@ -25,7 +25,7 @@ public enum UserType {
aliasMap.put("user", USER); aliasMap.put("user", USER);
} }
private String name; private final String name;
public static UserType getByAlias(String alias) { public static UserType getByAlias(String alias) {
return aliasMap.getOrDefault(alias, USER); return aliasMap.getOrDefault(alias, USER);

View File

@ -23,13 +23,13 @@ public interface IUser {
void setName(String name); void setName(String name);
UserType getUserType(); UserType getType();
void setUserType(UserType userType); void setType(UserType type);
UserStatus getUserStatus(); UserStatus getStatus();
void setUserStatus(UserStatus userStatus); void setStatus(UserStatus status);
String getPhone(); String getPhone();

View File

@ -10,6 +10,8 @@ import java.util.List;
@Setter @Setter
public abstract class TreeDomain<T extends TreeDomain<T>> extends AuditDomain { public abstract class TreeDomain<T extends TreeDomain<T>> extends AuditDomain {
public static final String ROOT = "0";
// 父id顶级是0 // 父id顶级是0
private String parentId; private String parentId;

View File

@ -28,12 +28,12 @@ public class User extends AuditDomain implements IUser {
/** /**
* 用户类型 * 用户类型
*/ */
private UserType userType; private UserType type;
/** /**
* 用户状态 * 用户状态
*/ */
private UserStatus userStatus; private UserStatus status;
/** /**
* 冗余的电话号码 * 冗余的电话号码
@ -83,7 +83,6 @@ public class User extends AuditDomain implements IUser {
/** /**
* 微信openId * 微信openId
*/ */
@Indexed(unique = true)
private String openId; private String openId;
/** /**

View File

@ -5,9 +5,7 @@ import com.flyfish.framework.config.properties.SecurityProperties;
import com.flyfish.framework.configuration.jwt.JwtSecurityContextRepository; import com.flyfish.framework.configuration.jwt.JwtSecurityContextRepository;
import com.flyfish.framework.configuration.jwt.TokenProvider; import com.flyfish.framework.configuration.jwt.TokenProvider;
import com.flyfish.framework.domain.UserQo; import com.flyfish.framework.domain.UserQo;
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.RoleType;
import com.flyfish.framework.enums.UserStatus; import com.flyfish.framework.enums.UserStatus;
import com.flyfish.framework.enums.UserType; import com.flyfish.framework.enums.UserType;
import com.flyfish.framework.handler.JsonAuthenticationFailureHandler; import com.flyfish.framework.handler.JsonAuthenticationFailureHandler;
@ -35,8 +33,6 @@ import org.springframework.security.web.server.context.ServerSecurityContextRepo
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import java.util.Arrays;
/** /**
* @author wangyu * @author wangyu
*/ */
@ -59,7 +55,6 @@ public class WebSecurityConfig {
return delegatingPasswordEncoder; return delegatingPasswordEncoder;
} }
@ConditionalOnProperty(value = "jwt.enable", havingValue = "true") @ConditionalOnProperty(value = "jwt.enable", havingValue = "true")
@Bean("contextRepository") @Bean("contextRepository")
public JwtSecurityContextRepository jwtSecurityContextRepository() { public JwtSecurityContextRepository jwtSecurityContextRepository() {
@ -121,24 +116,23 @@ public class WebSecurityConfig {
* 默认的初始化 * 默认的初始化
* *
* @param userService 用户服务 * @param userService 用户服务
* @param passwordEncoder 密码
* @return 结果 * @return 结果
*/ */
@Bean @Bean
@ConditionalOnMissingBean(UserInitializer.class) @ConditionalOnMissingBean(UserInitializer.class)
public UserInitializer userInitializer(UserService userService, PasswordEncoder passwordEncoder) { public UserInitializer userInitializer(UserService userService) {
return () -> { return () -> {
UserQo qo = new UserQo(); UserQo qo = new UserQo();
qo.setUserType(UserType.SUPER_ADMIN.name()); qo.setType(UserType.SUPER_ADMIN.name());
if (userService.count(qo) == 0) { if (userService.count(qo) == 0) {
// 初始化用户 // 初始化用户
User user = new User(); User user = new User();
user.setUsername("admin"); user.setUsername("admin");
user.setPassword(passwordEncoder.encode("admin123456")); user.setPassword("admin123456");
user.setUserType(UserType.SUPER_ADMIN); user.setType(UserType.SUPER_ADMIN);
user.setEnable(true); user.setEnable(true);
user.setApp(false); user.setApp(false);
user.setUserStatus(UserStatus.NORMAL); user.setStatus(UserStatus.NORMAL);
user.setPhone("10000000000"); user.setPhone("10000000000");
user.setName("超级管理员"); user.setName("超级管理员");
user.setCode("Administrator"); user.setCode("Administrator");

View File

@ -12,5 +12,5 @@ import org.springframework.web.bind.annotation.RestController;
*/ */
@RestController @RestController
@RequestMapping("/permissions") @RequestMapping("/permissions")
public class PermissionController extends BaseController<Permission, PermissionQo> { public class PermissionController extends TreeController<Permission, PermissionQo> {
} }

View File

@ -40,7 +40,7 @@ public class AdminUserDetails implements UserDetails, IUser {
* @return 结果 * @return 结果
*/ */
public static boolean isAdmin(User user) { public static boolean isAdmin(User user) {
return adminTypes.contains(user.getUserType()); return adminTypes.contains(user.getType());
} }
/** /**
@ -59,12 +59,12 @@ public class AdminUserDetails implements UserDetails, IUser {
/** /**
* 用户类型 * 用户类型
*/ */
private UserType userType; private UserType type;
/** /**
* 用户状态 * 用户状态
*/ */
private UserStatus userStatus; private UserStatus status;
/** /**
* 冗余的电话号码 * 冗余的电话号码
@ -126,7 +126,7 @@ public class AdminUserDetails implements UserDetails, IUser {
@Override @Override
@JsonIgnore @JsonIgnore
public boolean isAccountNonExpired() { public boolean isAccountNonExpired() {
return adminTypes.contains(getUserType()); return adminTypes.contains(getType());
} }
@Override @Override

View File

@ -3,10 +3,13 @@ package com.flyfish.framework.domain;
import com.flyfish.framework.builder.CriteriaBuilder; import com.flyfish.framework.builder.CriteriaBuilder;
import com.flyfish.framework.domain.base.NameLikeQo; import com.flyfish.framework.domain.base.NameLikeQo;
import com.flyfish.framework.domain.base.TreeQo;
import com.flyfish.framework.domain.po.Permission; import com.flyfish.framework.domain.po.Permission;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.util.List;
/** /**
* 权限查询实体 * 权限查询实体
* *
@ -14,12 +17,17 @@ import lombok.Setter;
*/ */
@Getter @Getter
@Setter @Setter
public class PermissionQo extends NameLikeQo<Permission> { public class PermissionQo extends TreeQo<Permission> {
private Boolean admin; private Boolean admin;
private String parentId;
private List<String> parentIds;
@Override @Override
public CriteriaBuilder<Permission> criteriaBuilder() { public CriteriaBuilder<Permission> criteriaBuilder() {
return super.criteriaBuilder().with("admin"); return super.criteriaBuilder().with("admin", "parentId")
.with("parentIds", "parentId", CriteriaBuilder.Builders.IN);
} }
} }

View File

@ -12,14 +12,18 @@ import lombok.*;
@NoArgsConstructor @NoArgsConstructor
public class UserQo extends NameLikeQo<User> { public class UserQo extends NameLikeQo<User> {
private String userType; private String type;
private String username; private String username;
private String password; private String password;
private String phone;
private String status;
@Override @Override
public CriteriaBuilder<User> criteriaBuilder() { public CriteriaBuilder<User> criteriaBuilder() {
return super.criteriaBuilder().with("userType", "username", "password"); return super.criteriaBuilder().with("type", "username", "password", "phone", "status");
} }
} }

View File

@ -16,7 +16,7 @@ import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.context.ServerSecurityContextRepository; import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
@ -45,13 +45,13 @@ public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
checkMap = new HashMap<>(); checkMap = new HashMap<>();
// 初始化用户校验规则 // 初始化用户校验规则
checkMap.put(user -> null != user.getEnable() && !user.getEnable() || checkMap.put(user -> null != user.getEnable() && !user.getEnable() ||
user.getUserStatus() == UserStatus.DISABLED, () -> new DisabledException("用户被禁用")); user.getStatus() == UserStatus.DISABLED, () -> new DisabledException("用户被禁用"));
checkMap.put(user -> user.getUserStatus() == UserStatus.LOCKED, checkMap.put(user -> user.getStatus() == UserStatus.LOCKED,
() -> new LockedException("账户已经锁定!请联系管理员修改密码!")); () -> new LockedException("账户已经锁定!请联系管理员修改密码!"));
} }
private final UserService service;
private final ReactiveUserService userService; private final ReactiveUserService userService;
private final PasswordEncoder passwordEncoder;
@Resource @Resource
private ServerSecurityContextRepository contextRepository; private ServerSecurityContextRepository contextRepository;
@ -62,7 +62,7 @@ public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
private void init() { private void init() {
UserDetailsRepositoryReactiveAuthenticationManager manager = UserDetailsRepositoryReactiveAuthenticationManager manager =
new UserDetailsRepositoryReactiveAuthenticationManager(this); new UserDetailsRepositoryReactiveAuthenticationManager(this);
manager.setPasswordEncoder(NoOpPasswordEncoder.getInstance()); manager.setPasswordEncoder(passwordEncoder);
authenticationManager = manager; authenticationManager = manager;
} }
@ -148,4 +148,24 @@ public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
.flatMap(context -> contextRepository.save(exchange, context)); .flatMap(context -> contextRepository.save(exchange, context));
} }
/**
* 发生错误时的处理
*
* @param exchange 交换
* @return 结果
*/
@Override
public Mono<User> error(ServerWebExchange exchange) {
return exchange.getFormData().flatMap(data -> userService.findByUsername(data.getFirst("username")))
.flatMap(user -> {
User updating = new User();
updating.setId(user.getId());
updating.setErrorCount(user.getErrorCount() + 1);
if (updating.getErrorCount() >= 5) {
updating.setStatus(UserStatus.LOCKED);
}
return userService.updateSelectiveById(updating);
});
}
} }

View File

@ -2,8 +2,10 @@ package com.flyfish.framework.service;
import com.flyfish.framework.domain.base.Qo; import com.flyfish.framework.domain.base.Qo;
import com.flyfish.framework.domain.po.User; import com.flyfish.framework.domain.po.User;
import com.flyfish.framework.enums.UserType;
import com.flyfish.framework.repository.UserRepository; import com.flyfish.framework.repository.UserRepository;
import com.flyfish.framework.service.impl.BaseServiceImpl; import com.flyfish.framework.service.impl.BaseServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -14,6 +16,9 @@ import java.util.Optional;
@Service @Service
public class UserService extends BaseServiceImpl<User> { public class UserService extends BaseServiceImpl<User> {
@Resource
private PasswordEncoder passwordEncoder;
/** /**
* 获取用户数据 * 获取用户数据
* *
@ -24,6 +29,31 @@ public class UserService extends BaseServiceImpl<User> {
return ((UserRepository) repository).findByUsername(username); return ((UserRepository) repository).findByUsername(username);
} }
@Override
public User create(User entity) {
if (null == entity.getId() && StringUtils.isNotBlank(entity.getPassword())) {
entity.setPassword(passwordEncoder.encode(entity.getPassword()));
}
if (null == entity.getType()) {
entity.setType(UserType.ADMIN);
}
if (null == entity.getEnable()) {
entity.setEnable(true);
}
if (null == entity.getApp()) {
entity.setApp(false);
}
if (null == entity.getCode()) {
entity.setCode(entity.getUsername());
}
return super.create(entity);
}
@Override
public User createSelective(User entity) {
return create(entity);
}
@Override @Override
public List<User> getList(Qo<User> query) { public List<User> getList(Qo<User> query) {
return super.getList(query); return super.getList(query);

View File

@ -30,7 +30,7 @@ public class EnumController {
private final Map<String, List<EnumValue>> values = new HashMap<>(); private final Map<String, List<EnumValue>> values = new HashMap<>();
public EnumController() { public EnumController() {
Reflections reflections = new Reflections("com.flyfish.project"); Reflections reflections = new Reflections("com.flyfish.project", "com.flyfish.framework");
// 得到Resource注解的类 // 得到Resource注解的类
Set<Class<? extends NamedEnum>> classSet = reflections.getSubTypesOf(NamedEnum.class); Set<Class<? extends NamedEnum>> classSet = reflections.getSubTypesOf(NamedEnum.class);
// 注入 // 注入

View File

@ -33,7 +33,8 @@ public abstract class TreeController<T extends TreeDomain<T>, Q extends Qo<T>> e
Map<String, List<T>> group = filtered.stream() Map<String, List<T>> group = filtered.stream()
.collect(Collectors.groupingBy(p -> StringUtils.defaultIfBlank(p.getParentId(), ""))); .collect(Collectors.groupingBy(p -> StringUtils.defaultIfBlank(p.getParentId(), "")));
// 第三步筛选一级树深度 // 第三步筛选一级树深度
List<T> topList = filtered.stream().filter(item -> item.getDepth() == 1).collect(Collectors.toList()); List<T> topList = filtered.stream().filter(item -> null != item && TreeDomain.ROOT.equals(item.getParentId()))
.collect(Collectors.toList());
// 第三步根据父id的map填充根tree // 第三步根据父id的map填充根tree
return Result.accept(applyChildren(topList, group)); return Result.accept(applyChildren(topList, group));
} }

View File

@ -1,6 +1,8 @@
package com.flyfish.framework.handler; package com.flyfish.framework.handler;
import com.flyfish.framework.bean.Result; import com.flyfish.framework.bean.Result;
import com.flyfish.framework.domain.po.User;
import com.flyfish.framework.service.MongoUserDetailsService;
import com.flyfish.framework.transform.DataBufferTransformer; import com.flyfish.framework.transform.DataBufferTransformer;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponse;
@ -12,6 +14,7 @@ import reactor.core.publisher.Mono;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional;
/** /**
* 基于json的登录失败包装详见Spring Security * 基于json的登录失败包装详见Spring Security
@ -41,11 +44,19 @@ public class JsonAuthenticationFailureHandler implements ServerAuthenticationFai
*/ */
@Override @Override
public Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) { public Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) {
return Mono.justOrEmpty(Optional.ofNullable(webFilterExchange.getExchange().getApplicationContext()))
.flatMap(applicationContext -> {
MongoUserDetailsService userDetailsService = applicationContext.getBean(MongoUserDetailsService.class);
Mono<User> mono = exception instanceof BadCredentialsException ? userDetailsService.error(webFilterExchange.getExchange())
: Mono.justOrEmpty(Optional.empty());
return mono.flatMap(user -> {
ServerHttpResponse response = webFilterExchange.getExchange().getResponse(); ServerHttpResponse response = webFilterExchange.getExchange().getResponse();
response.getHeaders().setContentType(MediaType.APPLICATION_JSON); response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return response.writeWith(Mono.fromCallable(() -> return response.writeWith(Mono.fromCallable(() ->
dataBufferTransformer.transform( dataBufferTransformer.transform(
Result.error(descriptionMap.getOrDefault(exception.getClass(), exception.getMessage())) Result.error(descriptionMap.getOrDefault(exception.getClass(), exception.getMessage()))
))); )));
});
});
} }
} }

View File

@ -1,5 +1,6 @@
package com.flyfish.framework.service; package com.flyfish.framework.service;
import com.flyfish.framework.domain.po.User;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
@ -41,4 +42,12 @@ public interface MongoUserDetailsService extends ReactiveUserDetailsService, Rea
* @return 结果 * @return 结果
*/ */
Mono<Void> logout(ServerWebExchange exchange); Mono<Void> logout(ServerWebExchange exchange);
/**
* 发生错误时的处理
*
* @param exchange 交换
* @return 结果
*/
Mono<User> error(ServerWebExchange exchange);
} }