Merge remote-tracking branch 'origin/master'
# Conflicts: # flyfish-user/src/main/java/com/flyfish/framework/service/MongoUserDetailsServiceImpl.java
This commit is contained in:
commit
76d9ed3f0a
@ -96,6 +96,11 @@ public class User extends AuditDomain implements IUser {
|
||||
*/
|
||||
private Integer errorCount = 0;
|
||||
|
||||
/**
|
||||
* 上次登录日期
|
||||
*/
|
||||
private Date lastTime;
|
||||
|
||||
@Override
|
||||
public User toUser() {
|
||||
return this;
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.flyfish.framework.logging.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -27,6 +26,8 @@ public class LoggingTextRegistry implements InitializingBean {
|
||||
this.mapping.put("UPDATE_ALL", "更新全部");
|
||||
this.mapping.put("DELETE", "删除");
|
||||
this.mapping.put("SYNC", "同步");
|
||||
this.mapping.put("LOGIN", "系统登录");
|
||||
this.mapping.put("LOGOUT", "退出登录");
|
||||
}
|
||||
|
||||
public String text(String code) {
|
||||
|
@ -15,6 +15,9 @@ import java.util.Date;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Log extends Domain {
|
||||
|
||||
// 日志类型
|
||||
private LogType type;
|
||||
|
||||
// 业务
|
||||
private String business;
|
||||
|
||||
|
@ -5,6 +5,7 @@ import com.flyfish.framework.domain.base.BaseQo;
|
||||
import com.flyfish.framework.domain.base.NameLikeQo;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -25,11 +26,18 @@ public class LogQo extends NameLikeQo<Log> {
|
||||
|
||||
private List<String> range;
|
||||
|
||||
private String type;
|
||||
|
||||
@Override
|
||||
public CriteriaBuilder<Log> criteriaBuilder() {
|
||||
return super.criteriaBuilder()
|
||||
.with("operator", CriteriaBuilder.Builders.LIKE)
|
||||
.with("module", "success")
|
||||
.with("type", "module", "success")
|
||||
.with("range", "startTime", CriteriaBuilder.Builders.DATE_RANGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sort sorts() {
|
||||
return Sort.by(Sort.Order.desc("startTime"));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
package com.flyfish.framework.logging.domain;
|
||||
|
||||
import com.flyfish.framework.enums.NamedEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum LogType implements NamedEnum {
|
||||
|
||||
OPERATION("操作"), AUTHENTICATION("登录");
|
||||
|
||||
private final String name;
|
||||
}
|
@ -3,6 +3,7 @@ package com.flyfish.framework.logging.service;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.flyfish.framework.logging.config.LoggingTextRegistry;
|
||||
import com.flyfish.framework.logging.domain.Log;
|
||||
import com.flyfish.framework.logging.domain.LogType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -38,6 +39,7 @@ public class LogManager implements DisposableBean {
|
||||
executorService.execute(() -> {
|
||||
// 构建日志
|
||||
Log log = new Log();
|
||||
log.setType(LogType.OPERATION);
|
||||
log.setSuccess(context.isSuccess());
|
||||
log.setBody(bodyString(context.getArgs()));
|
||||
log.setModule(context.getModule());
|
||||
|
@ -0,0 +1,116 @@
|
||||
package com.flyfish.framework.logging.service;
|
||||
|
||||
import com.flyfish.framework.domain.base.Domain;
|
||||
import com.flyfish.framework.domain.po.User;
|
||||
import com.flyfish.framework.logging.config.LoggingTextRegistry;
|
||||
import com.flyfish.framework.logging.domain.Log;
|
||||
import com.flyfish.framework.logging.domain.LogType;
|
||||
import com.flyfish.framework.service.AuthenticationLogger;
|
||||
import com.flyfish.framework.service.UserFindService;
|
||||
import com.flyfish.framework.utils.AuthenticationMessages;
|
||||
import com.flyfish.framework.utils.UserUtils;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 简单的认证日志
|
||||
*
|
||||
* @author wangyu
|
||||
*/
|
||||
@Service
|
||||
public class SimpleAuthenticationLogger implements AuthenticationLogger {
|
||||
|
||||
@Resource
|
||||
private LogService logService;
|
||||
@Resource
|
||||
private UserFindService userService;
|
||||
@Resource
|
||||
private LoggingTextRegistry registry;
|
||||
|
||||
/**
|
||||
* 记录成功
|
||||
*
|
||||
* @param user 用户
|
||||
*/
|
||||
@Override
|
||||
public void success(UserDetails user) {
|
||||
saveSuccess("LOGIN", "登录成功", user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录失败
|
||||
*
|
||||
* @param form 表单
|
||||
* @param exception 错误异常
|
||||
*/
|
||||
@Override
|
||||
public void failure(MultiValueMap<String, String> form, AuthenticationException exception) {
|
||||
saveError("LOGIN", "登录失败", form.getFirst("username"), AuthenticationMessages.message(exception));
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录退出
|
||||
*
|
||||
* @param user 用户
|
||||
*/
|
||||
@Override
|
||||
public void logout(UserDetails user) {
|
||||
saveSuccess("LOGOUT", "退出成功", user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作成功记录
|
||||
*
|
||||
* @param business 业务
|
||||
* @param message 信息
|
||||
* @param user 用户
|
||||
*/
|
||||
private void saveSuccess(String business, String message, UserDetails user) {
|
||||
User raw = UserUtils.toUser(user);
|
||||
Log log = new Log();
|
||||
log.setType(LogType.AUTHENTICATION);
|
||||
log.setSignature(business);
|
||||
log.setSuccess(true);
|
||||
log.setBody("为了安全,本内容隐藏");
|
||||
log.setModule("登录模块");
|
||||
log.setBusiness(registry.text(business));
|
||||
log.setResponse(message);
|
||||
log.setOperator(Optional.ofNullable(raw).map(Domain::getName).orElse("未知"));
|
||||
log.setPeriod(0L);
|
||||
log.setStartTime(new Date());
|
||||
// 写入日志
|
||||
logService.create(log);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存失败日志
|
||||
*
|
||||
* @param business 业务
|
||||
* @param message 消息
|
||||
* @param username 用户名
|
||||
* @param error 错误
|
||||
*/
|
||||
private void saveError(String business, String message, String username, String error) {
|
||||
Log log = new Log();
|
||||
log.setType(LogType.AUTHENTICATION);
|
||||
log.setSignature(business);
|
||||
log.setSuccess(false);
|
||||
log.setBody("某人尝试使用用户名'" + username + "',密码:******,进行登录");
|
||||
log.setModule("登录模块");
|
||||
log.setBusiness(registry.text(business));
|
||||
log.setError(error);
|
||||
log.setResponse(message);
|
||||
log.setOperator(userService.findByUsername(username).map(Domain::getName).orElse("未知"));
|
||||
log.setPeriod(0L);
|
||||
log.setStartTime(new Date());
|
||||
// 写入日志
|
||||
logService.create(log);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.flyfish.framework.config;
|
||||
|
||||
import com.flyfish.framework.domain.AdminUserDetails;
|
||||
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.transform.ResultDataTransformer;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.web.server.WebFilterExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 提供对象写入,登录登出审计
|
||||
*
|
||||
* @author wangyu
|
||||
*/
|
||||
public class AuthenticationAuditorImpl extends ResultDataTransformer implements AuthenticationAuditor {
|
||||
|
||||
@Resource
|
||||
private ReactiveUserService reactiveUserService;
|
||||
@Resource
|
||||
private AuthenticationLogger authenticationLogger;
|
||||
|
||||
/**
|
||||
* 检查用户异常,无异常,清空错误次数,设置上次登录
|
||||
*
|
||||
* @param user 用户
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> success(UserDetails user) {
|
||||
AdminUserDetails details = (AdminUserDetails) user;
|
||||
User updating = new User();
|
||||
updating.setId(details.getId());
|
||||
updating.setErrorCount(0);
|
||||
updating.setLastTime(new Date());
|
||||
return reactiveUserService.updateSelectiveById(updating)
|
||||
.then(Mono.fromRunnable(() -> authenticationLogger.success(user)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录失败的回调
|
||||
* 当输入错误的账号密码,记录错误次数,否则只记录日志
|
||||
*
|
||||
* @param webFilterExchange 请求信息
|
||||
* @param exception 异常
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> error(WebFilterExchange webFilterExchange, AuthenticationException exception) {
|
||||
return webFilterExchange.getExchange()
|
||||
.getFormData()
|
||||
.flatMap(data -> {
|
||||
// 当且仅当为凭据错误,尝试
|
||||
if (exception instanceof BadCredentialsException) {
|
||||
return reactiveUserService.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 reactiveUserService.updateSelectiveById(updating);
|
||||
})
|
||||
.map(user -> data);
|
||||
}
|
||||
return Mono.just(data);
|
||||
})
|
||||
.flatMap(data -> Mono.fromRunnable(() -> authenticationLogger.failure(data, exception)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> logout(UserDetails userDetails) {
|
||||
return Mono.fromRunnable(() -> authenticationLogger.logout(userDetails));
|
||||
}
|
||||
}
|
@ -12,8 +12,8 @@ import com.flyfish.framework.handler.JsonAuthenticationFailureHandler;
|
||||
import com.flyfish.framework.handler.JsonAuthenticationSuccessHandler;
|
||||
import com.flyfish.framework.handler.JsonLogoutSuccessHandler;
|
||||
import com.flyfish.framework.initializer.UserInitializer;
|
||||
import com.flyfish.framework.service.AuthenticationAuditor;
|
||||
import com.flyfish.framework.service.UserService;
|
||||
import com.flyfish.framework.transform.ResultDataTransformer;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@ -58,6 +58,11 @@ public class WebSecurityConfig {
|
||||
return delegatingPasswordEncoder;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationAuditor loginAuditor() {
|
||||
return new AuthenticationAuditorImpl();
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(value = "jwt.enable", havingValue = "true")
|
||||
@Bean("contextRepository")
|
||||
public JwtSecurityContextRepository jwtSecurityContextRepository() {
|
||||
@ -75,11 +80,6 @@ public class WebSecurityConfig {
|
||||
return new TokenProvider(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResultDataTransformer resultDataTransformer() {
|
||||
return new ResultDataTransformer();
|
||||
}
|
||||
|
||||
/**
|
||||
* spring安全拦截规则配置
|
||||
*
|
||||
@ -90,10 +90,10 @@ public class WebSecurityConfig {
|
||||
@Bean
|
||||
@SuppressWarnings("all")
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http,
|
||||
ResultDataTransformer dataTransformer,
|
||||
TokenProvider tokenProvider,
|
||||
SecurityProperties properties,
|
||||
ReactiveUserDetailsService userDetailsService) {
|
||||
ReactiveUserDetailsService userDetailsService,
|
||||
AuthenticationAuditor authenticationAuditor) {
|
||||
http
|
||||
.securityContextRepository(contextRepository())
|
||||
.authorizeExchange()
|
||||
@ -105,13 +105,13 @@ public class WebSecurityConfig {
|
||||
.formLogin() // 配置登录节点
|
||||
.authenticationManager(authenticationManager(properties, userDetailsService))
|
||||
.authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED))
|
||||
.authenticationFailureHandler(new JsonAuthenticationFailureHandler(dataTransformer))
|
||||
.authenticationSuccessHandler(new JsonAuthenticationSuccessHandler(dataTransformer))
|
||||
.authenticationSuccessHandler(new JsonAuthenticationSuccessHandler(authenticationAuditor))
|
||||
.authenticationFailureHandler(new JsonAuthenticationFailureHandler(authenticationAuditor))
|
||||
.requiresAuthenticationMatcher(pathMatchers(HttpMethod.POST, "/login", "/api/login"))
|
||||
.and()
|
||||
.logout()
|
||||
.logoutUrl("/api/logout")
|
||||
.logoutSuccessHandler(new JsonLogoutSuccessHandler(dataTransformer, tokenProvider))
|
||||
.logoutSuccessHandler(new JsonLogoutSuccessHandler(authenticationAuditor, tokenProvider))
|
||||
.and()
|
||||
.csrf().disable();
|
||||
return http.build();
|
||||
|
@ -138,7 +138,7 @@ public class AdminUserDetails implements UserDetails, IUser {
|
||||
@Override
|
||||
@JsonIgnore
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return isAccountNonExpired();
|
||||
return null == validDate || validDate.after(new Date());
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
|
@ -17,7 +17,6 @@ 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;
|
||||
@ -39,9 +38,6 @@ import java.util.function.Supplier;
|
||||
@Service
|
||||
public class MongoUserDetailsServiceImpl implements MongoUserDetailsService {
|
||||
|
||||
// 登录失败限制
|
||||
private static final int ERROR_LIMIT = 5;
|
||||
|
||||
// 存储用户校验规则的map
|
||||
private static final Map<Function<User, Boolean>, Supplier<AuthenticationException>> checkMap;
|
||||
|
||||
@ -152,24 +148,4 @@ 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() >= ERROR_LIMIT) {
|
||||
updating.setStatus(UserStatus.LOCKED);
|
||||
}
|
||||
return userService.updateSelectiveById(updating);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,9 @@ import com.flyfish.framework.service.impl.BaseServiceImpl;
|
||||
import com.flyfish.framework.utils.Assert;
|
||||
import com.flyfish.framework.utils.StrengthUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -16,7 +19,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class UserService extends BaseServiceImpl<User> {
|
||||
public class UserService extends BaseServiceImpl<User> implements UserFindService {
|
||||
|
||||
@Resource
|
||||
private PasswordEncoder passwordEncoder;
|
||||
@ -27,6 +30,7 @@ public class UserService extends BaseServiceImpl<User> {
|
||||
* @param username 用户
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public Optional<User> findByUsername(String username) {
|
||||
return ((UserRepository) repository).findByUsername(username);
|
||||
}
|
||||
@ -61,4 +65,5 @@ public class UserService extends BaseServiceImpl<User> {
|
||||
public List<User> getList(Qo<User> query) {
|
||||
return super.getList(query);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,20 +1,15 @@
|
||||
package com.flyfish.framework.handler;
|
||||
|
||||
import com.flyfish.framework.bean.Result;
|
||||
import com.flyfish.framework.service.MongoUserDetailsService;
|
||||
import com.flyfish.framework.transform.DataBufferTransformer;
|
||||
import com.flyfish.framework.service.AuthenticationAuditor;
|
||||
import com.flyfish.framework.utils.AuthenticationMessages;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.server.WebFilterExchange;
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 基于json的登录失败包装,详见Spring Security
|
||||
*
|
||||
@ -22,16 +17,11 @@ import java.util.Optional;
|
||||
*/
|
||||
public class JsonAuthenticationFailureHandler implements ServerAuthenticationFailureHandler {
|
||||
|
||||
private final String defaultMessage = "用户登录异常!";
|
||||
// 数据块工厂
|
||||
private final DataBufferTransformer<Result<?>> dataBufferTransformer;
|
||||
private final AuthenticationAuditor authenticationAuditor;
|
||||
|
||||
private final Map<Class<? extends AuthenticationException>, String> descriptionMap = new HashMap<>();
|
||||
|
||||
public JsonAuthenticationFailureHandler(DataBufferTransformer<Result<?>> dataBufferTransformer) {
|
||||
this.dataBufferTransformer = dataBufferTransformer;
|
||||
descriptionMap.put(BadCredentialsException.class, "用户名密码不正确!");
|
||||
// todo 剩下的都在userDetailsService
|
||||
public JsonAuthenticationFailureHandler(AuthenticationAuditor authenticationAuditor) {
|
||||
this.authenticationAuditor = authenticationAuditor;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,15 +35,8 @@ public class JsonAuthenticationFailureHandler implements ServerAuthenticationFai
|
||||
public Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) {
|
||||
ServerHttpResponse response = webFilterExchange.getExchange().getResponse();
|
||||
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
||||
return Mono.justOrEmpty(Optional.ofNullable(webFilterExchange.getExchange().getApplicationContext()))
|
||||
.flatMap(applicationContext -> {
|
||||
MongoUserDetailsService userDetailsService = applicationContext.getBean(MongoUserDetailsService.class);
|
||||
if (exception instanceof BadCredentialsException) {
|
||||
return userDetailsService.error(webFilterExchange.getExchange())
|
||||
.flatMap(user -> write(response, exception));
|
||||
}
|
||||
return write(response, exception);
|
||||
});
|
||||
// 记录错误次数,错误日志等,最后写入
|
||||
return authenticationAuditor.error(webFilterExchange, exception).then(write(response, exception));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,9 +47,6 @@ public class JsonAuthenticationFailureHandler implements ServerAuthenticationFai
|
||||
* @return 结果
|
||||
*/
|
||||
private Mono<Void> write(ServerHttpResponse response, AuthenticationException exception) {
|
||||
return response.writeWith(Mono.fromCallable(() ->
|
||||
dataBufferTransformer.transform(
|
||||
Result.error(descriptionMap.getOrDefault(exception.getClass(), exception.getMessage()))
|
||||
)));
|
||||
return response.writeWith(authenticationAuditor.transform(Result.error(AuthenticationMessages.message(exception))));
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
package com.flyfish.framework.handler;
|
||||
|
||||
import com.flyfish.framework.bean.Result;
|
||||
import com.flyfish.framework.transform.DataBufferTransformer;
|
||||
import com.flyfish.framework.service.AuthenticationAuditor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.web.server.WebFilterExchange;
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
|
||||
import reactor.core.publisher.Mono;
|
||||
@ -21,7 +21,7 @@ import reactor.core.publisher.Mono;
|
||||
public class JsonAuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler {
|
||||
|
||||
// 数据块工厂
|
||||
private final DataBufferTransformer<Result<?>> dataBufferTransformer;
|
||||
private final AuthenticationAuditor authenticationAuditor;
|
||||
|
||||
/**
|
||||
* 登录成功后要返回用户的基本信息,节省带宽
|
||||
@ -35,9 +35,9 @@ public class JsonAuthenticationSuccessHandler implements ServerAuthenticationSuc
|
||||
ServerHttpResponse response = webFilterExchange.getExchange().getResponse();
|
||||
HttpHeaders headers = response.getHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
// 查询
|
||||
return response.writeWith(ReactiveSecurityContextHolder.getContext().map(securityContext ->
|
||||
dataBufferTransformer.transform(Result.accept(securityContext.getAuthentication().getPrincipal()))
|
||||
));
|
||||
Object principal = authentication.getPrincipal();
|
||||
// 如果登录成功,清空错误次数,更新上次登录时间
|
||||
return authenticationAuditor.success((UserDetails) principal)
|
||||
.then(response.writeWith(authenticationAuditor.transform(Result.accept(principal))));
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,12 @@ package com.flyfish.framework.handler;
|
||||
|
||||
import com.flyfish.framework.bean.Result;
|
||||
import com.flyfish.framework.configuration.jwt.TokenProvider;
|
||||
import com.flyfish.framework.transform.DataBufferTransformer;
|
||||
import com.flyfish.framework.service.AuthenticationAuditor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.web.server.WebFilterExchange;
|
||||
import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;
|
||||
import reactor.core.publisher.Mono;
|
||||
@ -20,8 +21,9 @@ import reactor.core.publisher.Mono;
|
||||
public class JsonLogoutSuccessHandler implements ServerLogoutSuccessHandler {
|
||||
|
||||
// 数据块工厂
|
||||
private final DataBufferTransformer<Result<?>> dataBufferTransformer;
|
||||
private final AuthenticationAuditor authenticationAuditor;
|
||||
|
||||
// token提供者
|
||||
private final TokenProvider tokenProvider;
|
||||
|
||||
@Override
|
||||
@ -29,7 +31,12 @@ public class JsonLogoutSuccessHandler implements ServerLogoutSuccessHandler {
|
||||
ServerHttpResponse response = webFilterExchange.getExchange().getResponse();
|
||||
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
||||
tokenProvider.removeToken(webFilterExchange.getExchange());
|
||||
// 查询
|
||||
return response.writeWith(Mono.just(dataBufferTransformer.transform(Result.ok())));
|
||||
Object principal = authentication.getPrincipal();
|
||||
if ("anonymous".equals(principal)) {
|
||||
return response.writeWith(authenticationAuditor.transform(Result.ok()));
|
||||
}
|
||||
// 记录日志
|
||||
return authenticationAuditor.logout((UserDetails) authentication.getPrincipal())
|
||||
.then(response.writeWith(authenticationAuditor.transform(Result.ok())));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
package com.flyfish.framework.service;
|
||||
|
||||
import com.flyfish.framework.bean.Result;
|
||||
import com.flyfish.framework.transform.DataBufferTransformer;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.web.server.WebFilterExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 登录审计
|
||||
*
|
||||
* @author wangyu
|
||||
*/
|
||||
public interface AuthenticationAuditor extends DataBufferTransformer<Result<?>> {
|
||||
|
||||
/**
|
||||
* 登录成功的回调
|
||||
*
|
||||
* @param userDetails 用户信息
|
||||
*/
|
||||
Mono<Void> success(UserDetails userDetails);
|
||||
|
||||
/**
|
||||
* 登录失败的回调
|
||||
*
|
||||
* @param webFilterExchange 请求信息
|
||||
* @param exception 异常
|
||||
*/
|
||||
Mono<Void> error(WebFilterExchange webFilterExchange, AuthenticationException exception);
|
||||
|
||||
/**
|
||||
* 退出登录的回调
|
||||
*
|
||||
* @param userDetails 用户详情
|
||||
*/
|
||||
Mono<Void> logout(UserDetails userDetails);
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.flyfish.framework.service;
|
||||
|
||||
import com.flyfish.framework.domain.po.User;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* 认证日志者
|
||||
*
|
||||
* @author wangyu
|
||||
*/
|
||||
public interface AuthenticationLogger {
|
||||
|
||||
/**
|
||||
* 记录成功
|
||||
*
|
||||
* @param user 用户
|
||||
*/
|
||||
void success(UserDetails user);
|
||||
|
||||
/**
|
||||
* 记录失败
|
||||
*
|
||||
* @param form 表单
|
||||
* @param exception 错误异常
|
||||
*/
|
||||
void failure(MultiValueMap<String, String> form, AuthenticationException exception);
|
||||
|
||||
/**
|
||||
* 记录退出
|
||||
*
|
||||
* @param user 用户
|
||||
*/
|
||||
void logout(UserDetails user);
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
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;
|
||||
@ -42,12 +41,4 @@ public interface MongoUserDetailsService extends ReactiveUserDetailsService, Rea
|
||||
* @return 结果
|
||||
*/
|
||||
Mono<Void> logout(ServerWebExchange exchange);
|
||||
|
||||
/**
|
||||
* 发生错误时的处理
|
||||
*
|
||||
* @param exchange 交换
|
||||
* @return 结果
|
||||
*/
|
||||
Mono<User> error(ServerWebExchange exchange);
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package com.flyfish.framework.service;
|
||||
|
||||
|
||||
import com.flyfish.framework.domain.po.User;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 用户查找service
|
||||
* @author wangyu
|
||||
*/
|
||||
public interface UserFindService {
|
||||
|
||||
/**
|
||||
* 通过用户名查找
|
||||
* @param username 用户名
|
||||
* @return 结果
|
||||
*/
|
||||
Optional<User> findByUsername(String username);
|
||||
}
|
@ -1,13 +1,20 @@
|
||||
package com.flyfish.framework.transform;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 用于转换DataBuffer的工具
|
||||
*
|
||||
* @auther wangyu
|
||||
* @author wangyu
|
||||
*/
|
||||
public interface DataBufferTransformer<T> {
|
||||
|
||||
DataBuffer transform(T source);
|
||||
/**
|
||||
* 写入二进制数据,返回数据
|
||||
*
|
||||
* @param source 数据源
|
||||
* @return 结果
|
||||
*/
|
||||
Mono<DataBuffer> transform(T source);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import com.flyfish.framework.bean.Result;
|
||||
import com.flyfish.framework.utils.JacksonUtil;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
@ -21,10 +22,11 @@ public class ResultDataTransformer implements DataBufferTransformer<Result<?>> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBuffer transform(Result<?> source) {
|
||||
public Mono<DataBuffer> transform(Result<?> source) {
|
||||
return JacksonUtil.toJson(source)
|
||||
.map(result -> result.getBytes(Charset.defaultCharset()))
|
||||
.map(factory::wrap)
|
||||
.orElse(null);
|
||||
.map(Mono::just)
|
||||
.orElse(Mono.empty());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
package com.flyfish.framework.utils;
|
||||
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 认证消息
|
||||
* @author wangyu
|
||||
*/
|
||||
public interface AuthenticationMessages {
|
||||
|
||||
String DEFAULT_MESSAGE = "用户登录异常!";
|
||||
|
||||
// todo 剩下的都在userDetailsService
|
||||
Map<Class<? extends AuthenticationException>, String> DESCRIPTION_MAP = MapBuilder.<Class<? extends AuthenticationException>, String>builder()
|
||||
.put(BadCredentialsException.class, "用户名密码不正确!")
|
||||
.build();
|
||||
|
||||
/**
|
||||
* 消息构建,通过异常
|
||||
* @param exception 异常
|
||||
* @return 结果
|
||||
*/
|
||||
static String message(AuthenticationException exception) {
|
||||
return DESCRIPTION_MAP.getOrDefault(exception.getClass(), exception.getMessage());
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package com.flyfish.framework.utils;
|
||||
import com.flyfish.framework.domain.base.IUser;
|
||||
import com.flyfish.framework.domain.po.User;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
/**
|
||||
* 用户工具
|
||||
@ -20,4 +21,20 @@ public class UserUtils {
|
||||
public static User extractUser(SecurityContext securityContext) {
|
||||
return ((IUser) securityContext.getAuthentication().getPrincipal()).toUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换用户
|
||||
*
|
||||
* @param userDetails 用户详情
|
||||
* @return 结果
|
||||
*/
|
||||
public static User toUser(UserDetails userDetails) {
|
||||
if (null == userDetails) {
|
||||
return null;
|
||||
}
|
||||
if (userDetails instanceof IUser) {
|
||||
return ((IUser) userDetails).toUser();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user