测试环境配置及带租房的登录功能
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
package com.zf.foodpurchase.framework.config;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import com.zf.foodpurchase.framework.service.ClientUserDetailsService;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
public class ClientLoginAuthProvider extends AbstractUserDetailsAuthenticationProvider {
|
||||
|
||||
|
||||
private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
|
||||
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
/**
|
||||
* The password used to perform {@link PasswordEncoder#matches(CharSequence, String)}
|
||||
* on when the user is not found to avoid SEC-2056. This is necessary, because some
|
||||
* {@link PasswordEncoder} implementations will short circuit if the password is not
|
||||
* in a valid format.
|
||||
*/
|
||||
private volatile String userNotFoundEncodedPassword;
|
||||
|
||||
private ClientUserDetailsService userDetailsService;
|
||||
|
||||
private UserDetailsPasswordService userDetailsPasswordService;
|
||||
|
||||
public ClientLoginAuthProvider(ClientUserDetailsService clientUserDetailsService) {
|
||||
this.userDetailsService = clientUserDetailsService;
|
||||
setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void additionalAuthenticationChecks(UserDetails userDetails,
|
||||
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||
if (authentication.getCredentials() == null) {
|
||||
this.logger.debug("Failed to authenticate since no credentials provided");
|
||||
throw new BadCredentialsException(this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
String presentedPassword = authentication.getCredentials().toString();
|
||||
if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
|
||||
this.logger.debug("Failed to authenticate since password does not match stored value");
|
||||
throw new BadCredentialsException(this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAfterPropertiesSet() {
|
||||
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||
CustomUsernamePasswordAuthenticationToken auth = (CustomUsernamePasswordAuthenticationToken) authentication;
|
||||
|
||||
//多个参数
|
||||
UserDetails loadedUser = userDetailsService.loadUserByUsername(username,auth.getTenantId());
|
||||
|
||||
if (loadedUser == null) {
|
||||
throw new InternalAuthenticationServiceException(
|
||||
"UserDetailsService returned null, which is an interface contract violation");
|
||||
}
|
||||
return loadedUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
|
||||
UserDetails user) {
|
||||
boolean upgradeEncoding = this.userDetailsPasswordService != null
|
||||
&& this.passwordEncoder.upgradeEncoding(user.getPassword());
|
||||
if (upgradeEncoding) {
|
||||
String presentedPassword = authentication.getCredentials().toString();
|
||||
String newPassword = this.passwordEncoder.encode(presentedPassword);
|
||||
user = this.userDetailsPasswordService.updatePassword(user, newPassword);
|
||||
}
|
||||
return super.createSuccessAuthentication(principal, authentication, user);
|
||||
}
|
||||
|
||||
private void prepareTimingAttackProtection() {
|
||||
if (this.userNotFoundEncodedPassword == null) {
|
||||
this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) {
|
||||
if (authentication.getCredentials() != null) {
|
||||
String presentedPassword = authentication.getCredentials().toString();
|
||||
this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the PasswordEncoder instance to be used to encode and validate passwords. If
|
||||
* not set, the password will be compared using
|
||||
* {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}
|
||||
* @param passwordEncoder must be an instance of one of the {@code PasswordEncoder}
|
||||
* types.
|
||||
*/
|
||||
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
|
||||
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.userNotFoundEncodedPassword = null;
|
||||
}
|
||||
|
||||
protected PasswordEncoder getPasswordEncoder() {
|
||||
return this.passwordEncoder;
|
||||
}
|
||||
|
||||
public void setUserDetailsService(ClientUserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
protected ClientUserDetailsService getUserDetailsService() {
|
||||
return this.userDetailsService;
|
||||
}
|
||||
|
||||
public void setUserDetailsPasswordService(UserDetailsPasswordService userDetailsPasswordService) {
|
||||
this.userDetailsPasswordService = userDetailsPasswordService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.zf.foodpurchase.framework.config;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
|
||||
public class CustomUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
|
||||
private final String tenantId;
|
||||
|
||||
public CustomUsernamePasswordAuthenticationToken(Object principal, Object credentials, String tenantId) {
|
||||
super(principal, credentials);
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
public String getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.zf.foodpurchase.framework.config;
|
||||
|
||||
import com.zf.foodpurchase.framework.service.ClientUserDetailsService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -9,8 +10,12 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
@@ -23,28 +28,15 @@ import com.zf.foodpurchase.framework.security.filter.JwtAuthenticationTokenFilte
|
||||
import com.zf.foodpurchase.framework.security.handle.AuthenticationEntryPointImpl;
|
||||
import com.zf.foodpurchase.framework.security.handle.LogoutSuccessHandlerImpl;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* spring security配置
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||
@Configuration
|
||||
public class SecurityConfig
|
||||
{
|
||||
/**
|
||||
* 自定义用户认证逻辑
|
||||
*/
|
||||
@Autowired
|
||||
@Qualifier("userDetailsServiceImpl")
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
/**
|
||||
* 自定义微信小程序/APP认证逻辑类
|
||||
*/
|
||||
@Autowired
|
||||
@Qualifier("wxAppUserDetailsService")
|
||||
private UserDetailsService wxAppUserDetailsService;
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
/**
|
||||
* 认证失败处理类
|
||||
@@ -75,31 +67,22 @@ public class SecurityConfig
|
||||
*/
|
||||
@Autowired
|
||||
private PermitAllUrlProperties permitAllUrl;
|
||||
/**
|
||||
* 身份验证实现
|
||||
*/
|
||||
@Bean("authenticationManager")
|
||||
@Primary
|
||||
public AuthenticationManager authenticationManager()
|
||||
{
|
||||
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
|
||||
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
|
||||
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
|
||||
AuthenticationManager manager=new ProviderManager(daoAuthenticationProvider);
|
||||
return manager;
|
||||
|
||||
|
||||
@Resource
|
||||
private ClientUserDetailsService clientUserDetailsService;
|
||||
|
||||
@Bean
|
||||
public ClientLoginAuthProvider clientLoginAuthProvider(){
|
||||
ClientLoginAuthProvider provider = new ClientLoginAuthProvider(clientUserDetailsService);
|
||||
provider.setPasswordEncoder(bCryptPasswordEncoder());
|
||||
return provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* wxApp登录身份验证实现
|
||||
*/
|
||||
@Bean("wxAppAuthenticationManager")
|
||||
public AuthenticationManager wxAppAuthenticationManager()
|
||||
{
|
||||
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
|
||||
daoAuthenticationProvider.setUserDetailsService(wxAppUserDetailsService);
|
||||
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
|
||||
AuthenticationManager manager=new ProviderManager(daoAuthenticationProvider);
|
||||
return manager;
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,49 +100,45 @@ public class SecurityConfig
|
||||
* rememberMe | 允许通过remember-me登录的用户访问
|
||||
* authenticated | 用户登录后可访问
|
||||
*/
|
||||
@Bean
|
||||
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception
|
||||
{
|
||||
return httpSecurity
|
||||
// CSRF禁用,因为不使用session
|
||||
.csrf(csrf -> csrf.disable())
|
||||
// 禁用HTTP响应标头
|
||||
.headers((headersCustomizer) -> {
|
||||
headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin());
|
||||
})
|
||||
// 认证失败处理类
|
||||
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
|
||||
// 基于token,所以不需要session
|
||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
// 注解标记允许匿名访问的url
|
||||
.authorizeHttpRequests((requests) -> {
|
||||
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
|
||||
@Override
|
||||
protected void configure(HttpSecurity httpSecurity) throws Exception {
|
||||
|
||||
// 注解标记允许匿名访问的url
|
||||
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
|
||||
permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
|
||||
|
||||
httpSecurity
|
||||
// CSRF禁用,因为不使用session
|
||||
.csrf().disable()
|
||||
// 认证失败处理类
|
||||
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
|
||||
// 基于token,所以不需要session
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
|
||||
// 过滤请求
|
||||
.authorizeRequests()
|
||||
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
|
||||
requests.antMatchers("/login", "/register", "/captchaImage","/wx/user/mockLogin","/wx/user/login","/wxapp/userLogin/login").permitAll()
|
||||
// 静态资源,可匿名访问
|
||||
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
|
||||
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
|
||||
// 微信静态资源访问不被拦截
|
||||
.antMatchers("/wx/static/**","/wx/payApi/").permitAll()
|
||||
// 部分接口开放,不受权限访问限制
|
||||
.antMatchers("/wx/area/**").permitAll()
|
||||
// 微信体验功能,部分接口权限放开
|
||||
// 首页查询商品查询,轮播图查询,类别查询,按类别查询商品接口
|
||||
.antMatchers("/wx/products/indexProducts","/wx/dict/type/product_category","/wx/products/category","/wx/carousel/list").permitAll()
|
||||
// 临时放开权限校验,共开发测试使用
|
||||
// .antMatchers("/wx/**").permitAll()
|
||||
.antMatchers("/wx/payApi/**").permitAll()
|
||||
// 除上面外的所有请求全部需要鉴权认证
|
||||
.anyRequest().authenticated();
|
||||
})
|
||||
// 添加Logout filter
|
||||
.logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler))
|
||||
// 添加JWT filter
|
||||
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
// 添加CORS filter
|
||||
.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
|
||||
.addFilterBefore(corsFilter, LogoutFilter.class)
|
||||
.build();
|
||||
.antMatchers("/login", "/register", "/captchaImage","/wx/user/mockLogin","/wx/user/login","/wxapp/userLogin/login").permitAll()
|
||||
// 静态资源,可匿名访问
|
||||
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
|
||||
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
|
||||
// 微信静态资源访问不被拦截
|
||||
.antMatchers("/wx/static/**","/wx/payApi/").permitAll()
|
||||
// 部分接口开放,不受权限访问限制
|
||||
.antMatchers("/wx/area/**").permitAll()
|
||||
// 微信体验功能,部分接口权限放开
|
||||
// 首页查询商品查询,轮播图查询,类别查询,按类别查询商品接口
|
||||
.antMatchers("/wx/products/indexProducts","/wx/dict/type/product_category","/wx/products/category","/wx/carousel/list").permitAll()
|
||||
// 临时放开权限校验,共开发测试使用
|
||||
// .antMatchers("/wx/**").permitAll()
|
||||
.antMatchers("/wx/payApi/**").permitAll()
|
||||
// 除上面外的所有请求全部需要鉴权认证
|
||||
.anyRequest().authenticated();
|
||||
// 添加Logout filter
|
||||
httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
|
||||
// 添加JWT filter
|
||||
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,4 +149,11 @@ public class SecurityConfig
|
||||
{
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
// auth.inMemoryAuthentication().passwordEncoder(bCryptPasswordEncoder());
|
||||
//auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
|
||||
auth.authenticationProvider(clientLoginAuthProvider());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.zf.foodpurchase.framework.service;
|
||||
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
public interface ClientUserDetailsService {
|
||||
UserDetails loadUserByUsername(String username, String tenantId) throws UsernameNotFoundException;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.zf.foodpurchase.framework.service.impl;
|
||||
|
||||
|
||||
import com.zf.foodpurchase.common.core.domain.entity.SysUser;
|
||||
import com.zf.foodpurchase.common.core.domain.model.LoginUser;
|
||||
import com.zf.foodpurchase.common.enums.UserStatus;
|
||||
import com.zf.foodpurchase.common.exception.ServiceException;
|
||||
import com.zf.foodpurchase.common.utils.StringUtils;
|
||||
import com.zf.foodpurchase.framework.web.service.SysPasswordService;
|
||||
import com.zf.foodpurchase.framework.web.service.SysPermissionService;
|
||||
import com.zf.foodpurchase.system.mapper.SysDeptMapper;
|
||||
import com.zf.foodpurchase.system.mapper.SysRoleDeptMapper;
|
||||
import com.zf.foodpurchase.system.mapper.SysUserMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.zf.foodpurchase.framework.service.ClientUserDetailsService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Service("clientUserDetailsService")
|
||||
public class ClientUserDetailsServiceImpl implements ClientUserDetailsService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ClientUserDetailsServiceImpl.class);
|
||||
|
||||
@Resource
|
||||
private SysUserMapper sysUserMapper;
|
||||
|
||||
@Autowired
|
||||
private SysPasswordService passwordService;
|
||||
|
||||
@Autowired
|
||||
private SysPermissionService permissionService;
|
||||
|
||||
@Autowired
|
||||
private SysRoleDeptMapper roleDeptMapper;
|
||||
|
||||
@Autowired
|
||||
private SysDeptMapper deptMapper;
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username, String tenantId) throws UsernameNotFoundException {
|
||||
// 此处即可自定义查询用户
|
||||
SysUser user = sysUserMapper.selectUserByUserNameAndTenantId(username,tenantId);
|
||||
if (StringUtils.isNull(user))
|
||||
{
|
||||
log.info("登录用户:{} 不存在.", username);
|
||||
throw new ServiceException("登录用户:" + username + " 不存在");
|
||||
}
|
||||
else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
|
||||
{
|
||||
log.info("登录用户:{} 已被删除.", username);
|
||||
throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
|
||||
}
|
||||
else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
|
||||
{
|
||||
log.info("登录用户:{} 已被停用.", username);
|
||||
throw new ServiceException("对不起,您的账号:" + username + " 已停用");
|
||||
}
|
||||
|
||||
passwordService.validate(user);
|
||||
|
||||
return createLoginUser(user);
|
||||
}
|
||||
|
||||
public UserDetails createLoginUser(SysUser user)
|
||||
{
|
||||
return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.zf.foodpurchase.framework.web.service;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.zf.foodpurchase.common.constant.CacheConstants;
|
||||
import com.zf.foodpurchase.common.constant.Constants;
|
||||
import com.zf.foodpurchase.common.constant.UserConstants;
|
||||
import com.zf.foodpurchase.common.core.domain.AjaxResult;
|
||||
import com.zf.foodpurchase.common.core.domain.entity.SysTenantVo;
|
||||
import com.zf.foodpurchase.common.core.domain.entity.SysUser;
|
||||
import com.zf.foodpurchase.common.core.domain.model.LoginUser;
|
||||
import com.zf.foodpurchase.common.core.redis.RedisCache;
|
||||
@@ -12,10 +15,12 @@ import com.zf.foodpurchase.common.utils.DateUtils;
|
||||
import com.zf.foodpurchase.common.utils.MessageUtils;
|
||||
import com.zf.foodpurchase.common.utils.StringUtils;
|
||||
import com.zf.foodpurchase.common.utils.ip.IpUtils;
|
||||
import com.zf.foodpurchase.framework.config.CustomUsernamePasswordAuthenticationToken;
|
||||
import com.zf.foodpurchase.framework.manager.AsyncManager;
|
||||
import com.zf.foodpurchase.framework.manager.factory.AsyncFactory;
|
||||
import com.zf.foodpurchase.framework.security.context.AuthenticationContextHolder;
|
||||
import com.zf.foodpurchase.system.service.ISysConfigService;
|
||||
import com.zf.foodpurchase.system.service.ISysTenantService;
|
||||
import com.zf.foodpurchase.system.service.ISysUserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
@@ -26,6 +31,9 @@ import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 登录校验方法
|
||||
@@ -39,7 +47,6 @@ public class SysLoginService
|
||||
private TokenService tokenService;
|
||||
|
||||
@Resource
|
||||
@Qualifier("authenticationManager")
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Autowired
|
||||
@@ -51,26 +58,39 @@ public class SysLoginService
|
||||
@Autowired
|
||||
private ISysConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private ISysTenantService tenantService;
|
||||
|
||||
/**
|
||||
* 登录验证
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param password 密码
|
||||
* @param code 验证码
|
||||
* @param uuid 唯一标识
|
||||
* @return 结果
|
||||
*/
|
||||
public String login(String username, String password, String code, String uuid)
|
||||
public AjaxResult login(String tenantId, String username, String password)
|
||||
{
|
||||
// 验证码校验
|
||||
validateCaptcha(username, code, uuid);
|
||||
//validateCaptcha(username, code, uuid);
|
||||
// 登录前置校验
|
||||
loginPreCheck(username, password);
|
||||
//loginPreCheck(username, password);
|
||||
if (StringUtils.isBlank(tenantId)) {
|
||||
return AjaxResult.error("请选择租户");
|
||||
}
|
||||
SysTenantVo tenant = tenantService.queryByTenantId(tenantId);
|
||||
if (ObjectUtil.isNull(tenant)) {
|
||||
return AjaxResult.error("租户不存在");
|
||||
} else if ("1".equals(tenant.getStatus())) {
|
||||
return AjaxResult.error("租户已停用");
|
||||
} else if (ObjectUtil.isNotNull(tenant.getExpireTime())
|
||||
&& new Date().after(tenant.getExpireTime())) {
|
||||
return AjaxResult.error("租户已过期");
|
||||
}
|
||||
// 用户验证
|
||||
Authentication authentication = null;
|
||||
try
|
||||
{
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
|
||||
CustomUsernamePasswordAuthenticationToken authenticationToken = new CustomUsernamePasswordAuthenticationToken(username, password,tenantId);
|
||||
AuthenticationContextHolder.setContext(authenticationToken);
|
||||
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
|
||||
authentication = authenticationManager.authenticate(authenticationToken);
|
||||
@@ -96,7 +116,12 @@ public class SysLoginService
|
||||
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
|
||||
recordLoginInfo(loginUser.getUserId());
|
||||
// 生成token
|
||||
return tokenService.createToken(loginUser);
|
||||
String token = tokenService.createToken(loginUser);
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(Constants.TOKEN, token);
|
||||
map.put("userId",loginUser.getUserId());
|
||||
return AjaxResult.success(map);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
package com.zf.foodpurchase.framework.web.service;
|
||||
|
||||
import com.zf.foodpurchase.common.constant.CacheConstants;
|
||||
import com.zf.foodpurchase.common.constant.Constants;
|
||||
import com.zf.foodpurchase.common.constant.UserConstants;
|
||||
import com.zf.foodpurchase.common.core.domain.entity.SysUser;
|
||||
import com.zf.foodpurchase.common.core.domain.entity.WxAppUserVO;
|
||||
import com.zf.foodpurchase.common.core.domain.model.LoginUser;
|
||||
import com.zf.foodpurchase.common.core.domain.model.WxAppLoginBody;
|
||||
import com.zf.foodpurchase.common.core.domain.model.WxAppLoginUser;
|
||||
import com.zf.foodpurchase.common.core.redis.RedisCache;
|
||||
import com.zf.foodpurchase.common.exception.ServiceException;
|
||||
import com.zf.foodpurchase.common.exception.user.*;
|
||||
import com.zf.foodpurchase.common.utils.DateUtils;
|
||||
import com.zf.foodpurchase.common.utils.MessageUtils;
|
||||
import com.zf.foodpurchase.common.utils.StringUtils;
|
||||
import com.zf.foodpurchase.common.utils.ip.IpUtils;
|
||||
import com.zf.foodpurchase.fdsafe.entity.WxAppUser;
|
||||
import com.zf.foodpurchase.fdsafe.service.IWxAppUserService;
|
||||
import com.zf.foodpurchase.framework.manager.AsyncManager;
|
||||
import com.zf.foodpurchase.framework.manager.factory.AsyncFactory;
|
||||
import com.zf.foodpurchase.framework.security.context.AuthenticationContextHolder;
|
||||
import com.zf.foodpurchase.system.service.ISysConfigService;
|
||||
import com.zf.foodpurchase.system.service.ISysUserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 登录校验方法
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public class WxAppLoginService
|
||||
{
|
||||
@Autowired
|
||||
private WxAppTokenService tokenService;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("wxAppAuthenticationManager")
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
@Autowired
|
||||
private IWxAppUserService wxAppUserService;
|
||||
|
||||
@Autowired
|
||||
private ISysConfigService configService;
|
||||
|
||||
/**
|
||||
* 登录验证
|
||||
*
|
||||
* @param wxAppLoginBody 登录对象
|
||||
* @return 结果
|
||||
*/
|
||||
public String login(WxAppLoginBody wxAppLoginBody)
|
||||
{
|
||||
String username = wxAppLoginBody.getUsername();
|
||||
String password = wxAppLoginBody.getPassword();
|
||||
// 验证码校验
|
||||
// validateCaptcha(username, code, uuid);
|
||||
// 登录前置校验
|
||||
loginPreCheck(username, password);
|
||||
// 用户验证
|
||||
Authentication authentication = null;
|
||||
try
|
||||
{
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
|
||||
AuthenticationContextHolder.setContext(authenticationToken);
|
||||
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
|
||||
authentication = authenticationManager.authenticate(authenticationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e instanceof BadCredentialsException)
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
|
||||
throw new UserPasswordNotMatchException();
|
||||
}
|
||||
else
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
|
||||
throw new ServiceException(e.getMessage());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
AuthenticationContextHolder.clearContext();
|
||||
}
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
|
||||
WxAppLoginUser loginUser = (WxAppLoginUser) authentication.getPrincipal();
|
||||
recordLoginInfo(loginUser.getUserId());
|
||||
// 生成token
|
||||
return tokenService.createWxAppToken(loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验验证码
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param code 验证码
|
||||
* @param uuid 唯一标识
|
||||
* @return 结果
|
||||
*/
|
||||
public void validateCaptcha(String username, String code, String uuid)
|
||||
{
|
||||
boolean captchaEnabled = configService.selectCaptchaEnabled();
|
||||
if (captchaEnabled)
|
||||
{
|
||||
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
|
||||
String captcha = redisCache.getCacheObject(verifyKey);
|
||||
if (captcha == null)
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
|
||||
throw new CaptchaExpireException();
|
||||
}
|
||||
redisCache.deleteObject(verifyKey);
|
||||
if (!code.equalsIgnoreCase(captcha))
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
|
||||
throw new CaptchaException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录前置校验
|
||||
* @param username 用户名
|
||||
* @param password 用户密码
|
||||
*/
|
||||
public void loginPreCheck(String username, String password)
|
||||
{
|
||||
// 用户名或密码为空 错误
|
||||
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
|
||||
throw new UserNotExistsException();
|
||||
}
|
||||
// 密码如果不在指定范围内 错误
|
||||
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|
||||
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH)
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
|
||||
throw new UserPasswordNotMatchException();
|
||||
}
|
||||
// 用户名不在指定范围内 错误
|
||||
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|
||||
|| username.length() > UserConstants.USERNAME_MAX_LENGTH)
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
|
||||
throw new UserPasswordNotMatchException();
|
||||
}
|
||||
// IP黑名单校验
|
||||
String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
|
||||
if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
|
||||
throw new BlackListException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登录信息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
public void recordLoginInfo(Long userId)
|
||||
{
|
||||
WxAppUser user = new WxAppUser();
|
||||
user.setId(userId);
|
||||
user.setLoginTime(DateUtils.getNowDate());
|
||||
user.setLoginIp(IpUtils.getIpAddr());
|
||||
user.setUpdateTime(DateUtils.getNowDate());
|
||||
wxAppUserService.updateById(user);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user