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
@AllArgsConstructor
public enum RoleType {
public enum RoleType implements NamedEnum {
PC("PC端"), MOBILE("移动端");
private String name;
private final String name;
}

View File

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

View File

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

View File

@ -23,13 +23,13 @@ public interface IUser {
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();

View File

@ -10,6 +10,8 @@ import java.util.List;
@Setter
public abstract class TreeDomain<T extends TreeDomain<T>> extends AuditDomain {
public static final String ROOT = "0";
// 父id顶级是0
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
*/
@Indexed(unique = true)
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.TokenProvider;
import com.flyfish.framework.domain.UserQo;
import com.flyfish.framework.domain.po.Role;
import com.flyfish.framework.domain.po.User;
import com.flyfish.framework.enums.RoleType;
import com.flyfish.framework.enums.UserStatus;
import com.flyfish.framework.enums.UserType;
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.util.matcher.ServerWebExchangeMatchers;
import java.util.Arrays;
/**
* @author wangyu
*/
@ -59,7 +55,6 @@ public class WebSecurityConfig {
return delegatingPasswordEncoder;
}
@ConditionalOnProperty(value = "jwt.enable", havingValue = "true")
@Bean("contextRepository")
public JwtSecurityContextRepository jwtSecurityContextRepository() {
@ -121,24 +116,23 @@ public class WebSecurityConfig {
* 默认的初始化
*
* @param userService 用户服务
* @param passwordEncoder 密码
* @return 结果
*/
@Bean
@ConditionalOnMissingBean(UserInitializer.class)
public UserInitializer userInitializer(UserService userService, PasswordEncoder passwordEncoder) {
public UserInitializer userInitializer(UserService userService) {
return () -> {
UserQo qo = new UserQo();
qo.setUserType(UserType.SUPER_ADMIN.name());
qo.setType(UserType.SUPER_ADMIN.name());
if (userService.count(qo) == 0) {
// 初始化用户
User user = new User();
user.setUsername("admin");
user.setPassword(passwordEncoder.encode("admin123456"));
user.setUserType(UserType.SUPER_ADMIN);
user.setPassword("admin123456");
user.setType(UserType.SUPER_ADMIN);
user.setEnable(true);
user.setApp(false);
user.setUserStatus(UserStatus.NORMAL);
user.setStatus(UserStatus.NORMAL);
user.setPhone("10000000000");
user.setName("超级管理员");
user.setCode("Administrator");

View File

@ -12,5 +12,5 @@ import org.springframework.web.bind.annotation.RestController;
*/
@RestController
@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 结果
*/
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
@JsonIgnore
public boolean isAccountNonExpired() {
return adminTypes.contains(getUserType());
return adminTypes.contains(getType());
}
@Override

View File

@ -3,10 +3,13 @@ package com.flyfish.framework.domain;
import com.flyfish.framework.builder.CriteriaBuilder;
import com.flyfish.framework.domain.base.NameLikeQo;
import com.flyfish.framework.domain.base.TreeQo;
import com.flyfish.framework.domain.po.Permission;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
/**
* 权限查询实体
*
@ -14,12 +17,17 @@ import lombok.Setter;
*/
@Getter
@Setter
public class PermissionQo extends NameLikeQo<Permission> {
public class PermissionQo extends TreeQo<Permission> {
private Boolean admin;
private String parentId;
private List<String> parentIds;
@Override
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
public class UserQo extends NameLikeQo<User> {
private String userType;
private String type;
private String username;
private String password;
private String phone;
private String status;
@Override
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.userdetails.UserDetails;
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.stereotype.Service;
import org.springframework.web.server.ServerWebExchange;
@ -45,13 +45,13 @@ public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
checkMap = new HashMap<>();
// 初始化用户校验规则
checkMap.put(user -> null != user.getEnable() && !user.getEnable() ||
user.getUserStatus() == UserStatus.DISABLED, () -> new DisabledException("用户被禁用"));
checkMap.put(user -> user.getUserStatus() == UserStatus.LOCKED,
user.getStatus() == UserStatus.DISABLED, () -> new DisabledException("用户被禁用"));
checkMap.put(user -> user.getStatus() == UserStatus.LOCKED,
() -> new LockedException("账户已经锁定!请联系管理员修改密码!"));
}
private final UserService service;
private final ReactiveUserService userService;
private final PasswordEncoder passwordEncoder;
@Resource
private ServerSecurityContextRepository contextRepository;
@ -62,7 +62,7 @@ public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
private void init() {
UserDetailsRepositoryReactiveAuthenticationManager manager =
new UserDetailsRepositoryReactiveAuthenticationManager(this);
manager.setPasswordEncoder(NoOpPasswordEncoder.getInstance());
manager.setPasswordEncoder(passwordEncoder);
authenticationManager = manager;
}
@ -148,4 +148,24 @@ public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
.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.po.User;
import com.flyfish.framework.enums.UserType;
import com.flyfish.framework.repository.UserRepository;
import com.flyfish.framework.service.impl.BaseServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@ -14,6 +16,9 @@ import java.util.Optional;
@Service
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);
}
@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
public List<User> getList(Qo<User> query) {
return super.getList(query);

View File

@ -30,7 +30,7 @@ public class EnumController {
private final Map<String, List<EnumValue>> values = new HashMap<>();
public EnumController() {
Reflections reflections = new Reflections("com.flyfish.project");
Reflections reflections = new Reflections("com.flyfish.project", "com.flyfish.framework");
// 得到Resource注解的类
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()
.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
return Result.accept(applyChildren(topList, group));
}

View File

@ -1,6 +1,8 @@
package com.flyfish.framework.handler;
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 org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
@ -12,6 +14,7 @@ import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
* 基于json的登录失败包装详见Spring Security
@ -41,11 +44,19 @@ public class JsonAuthenticationFailureHandler implements ServerAuthenticationFai
*/
@Override
public Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) {
ServerHttpResponse response = webFilterExchange.getExchange().getResponse();
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return response.writeWith(Mono.fromCallable(() ->
dataBufferTransformer.transform(
Result.error(descriptionMap.getOrDefault(exception.getClass(), exception.getMessage()))
)));
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();
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return response.writeWith(Mono.fromCallable(() ->
dataBufferTransformer.transform(
Result.error(descriptionMap.getOrDefault(exception.getClass(), exception.getMessage()))
)));
});
});
}
}

View File

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