Browse Source

修复注册不成功的问题

fangzhen 7 months ago
parent
commit
5a147474a8

+ 13 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java

@@ -3,6 +3,7 @@ package com.ruoyi.web.controller.system;
 import java.util.List;
 import java.util.Set;
 
+import com.ruoyi.common.core.domain.model.LoginByTelephoneBody;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -96,4 +97,16 @@ public class SysLoginController {
         List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
         return AjaxResult.success(menuService.buildMenus(menus));
     }
+
+    @PostMapping("/phonelogin")
+    public AjaxResult phoneLogin(@RequestBody LoginByTelephoneBody loginBody){
+
+        AjaxResult ajax = AjaxResult.success();
+        // 生成令牌
+        String token = loginService.loginByTelephone(loginBody.getPhone(), loginBody.getCode(),
+                loginBody.getUuid());
+        ajax.put(Constants.TOKEN, token);
+        return ajax;
+
+    }
 }

+ 2 - 0
ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java

@@ -170,6 +170,8 @@ public class Constants
      */
     public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" };
 
+    public static final String CUSTOM_LOGIN_SMS = "custom:";
+
     /**
      * 定时任务违规的字符
      */

+ 30 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginByTelephoneBody.java

@@ -0,0 +1,30 @@
+package com.ruoyi.common.core.domain.model;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class LoginByTelephoneBody {
+
+
+    /**
+     * 手机号
+     */
+    private String phone;
+
+
+    /**
+     * 验证码
+     */
+    private String code;
+
+
+    /**
+     * 唯一标识
+     */
+    private String uuid;
+}

+ 57 - 40
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java

@@ -1,8 +1,11 @@
 package com.ruoyi.framework.config;
 
+import com.ruoyi.framework.security.filter.CustomLoginAuthenticationProvider;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
 import org.springframework.http.HttpMethod;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.ProviderManager;
@@ -23,19 +26,26 @@ import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
 
 /**
  * spring security配置
- * 
+ *
  * @author ruoyi
  */
 @EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
 @Configuration
-public class SecurityConfig
-{
+public class SecurityConfig {
     /**
      * 自定义用户认证逻辑
      */
     @Autowired
+    @Qualifier("UserDetailsServiceImpl")
     private UserDetailsService userDetailsService;
-    
+
+    /**
+     * 手机验证码登录
+     */
+    @Autowired
+    @Qualifier("userDetailsByPhoneNumber")
+    private UserDetailsService userDetailsByPhoneNumber;
+
     /**
      * 认证失败处理类
      */
@@ -53,7 +63,7 @@ public class SecurityConfig
      */
     @Autowired
     private JwtAuthenticationTokenFilter authenticationTokenFilter;
-    
+
     /**
      * 跨域过滤器
      */
@@ -70,14 +80,23 @@ public class SecurityConfig
      * 身份验证实现
      */
     @Bean
-    public AuthenticationManager authenticationManager()
-    {
+    @Primary
+    public AuthenticationManager authenticationManager() {
         DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
         daoAuthenticationProvider.setUserDetailsService(userDetailsService);
         daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
         return new ProviderManager(daoAuthenticationProvider);
     }
 
+    /**
+     * 身份验证(手机号登录)
+     */
+    @Bean
+    public AuthenticationManager authenticationPhoneManager() {
+        CustomLoginAuthenticationProvider customLoginAuthenticationProvider = new CustomLoginAuthenticationProvider(userDetailsByPhoneNumber);
+        return new ProviderManager(customLoginAuthenticationProvider);
+    }
+
     /**
      * anyRequest          |   匹配所有请求路径
      * access              |   SpringEl表达式结果为true时可以访问
@@ -94,46 +113,44 @@ public class SecurityConfig
      * authenticated       |   用户登录后可访问
      */
     @Bean
-    protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception
-    {
+    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());
-                // 对于登录login 注册register 验证码captchaImage 允许匿名访问
-                requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
-                    // 静态资源,可匿名访问
-                    .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
-                    .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**","/websocket/**").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();
+                // 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());
+                    // 对于登录login 注册register 验证码captchaImage 允许匿名访问
+                    requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
+                            // 静态资源,可匿名访问
+                            .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
+                            .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**", "/websocket/**").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();
     }
 
     /**
      * 强散列哈希加密实现
      */
     @Bean
-    public BCryptPasswordEncoder bCryptPasswordEncoder()
-    {
+    public BCryptPasswordEncoder bCryptPasswordEncoder() {
         return new BCryptPasswordEncoder();
     }
 }

+ 40 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/CustomLoginAuthenticationProvider.java

@@ -0,0 +1,40 @@
+package com.ruoyi.framework.security.filter;
+
+import com.ruoyi.common.constant.Constants;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+/**
+ * 自定义登录扩展
+ */
+public class CustomLoginAuthenticationProvider extends DaoAuthenticationProvider {
+
+    public CustomLoginAuthenticationProvider(UserDetailsService userDetailsService) {
+        super();
+        setUserDetailsService(userDetailsService);
+    }
+
+    @Override
+    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
+        if (authentication.getCredentials() == null) {
+            this.logger.debug("Authentication failed: no credentials provided");
+            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
+        } else {
+            String password = authentication.getCredentials().toString();
+            if (Constants.CUSTOM_LOGIN_SMS.equals(password)) {
+                //短信登录,不验证密码
+            } else {
+                BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+                if (!passwordEncoder.matches(password, userDetails.getPassword())) {
+                    this.logger.debug("Authentication failed: password does not match stored value");
+                    throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
+                }
+            }
+        }
+    }
+}

+ 53 - 2
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java

@@ -2,6 +2,7 @@ package com.ruoyi.framework.web.service;
 
 import javax.annotation.Resource;
 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;
@@ -40,8 +41,8 @@ public class SysLoginService
     @Autowired
     private TokenService tokenService;
 
-    @Resource
-    private AuthenticationManager authenticationManager;
+//    @Resource
+//    private AuthenticationManager authenticationManager;
 
     @Autowired
     private RedisCache redisCache;
@@ -52,6 +53,15 @@ public class SysLoginService
     @Autowired
     private ISysConfigService configService;
 
+    private final AuthenticationManager authenticationManager;
+    private final AuthenticationManager authenticationPhoneManager;
+
+    public SysLoginService(@Qualifier("authenticationManager") AuthenticationManager authenticationManager,
+                           @Qualifier("authenticationPhoneManager") AuthenticationManager authenticationPhoneManager) {
+        this.authenticationManager = authenticationManager;
+        this.authenticationPhoneManager = authenticationPhoneManager;
+    }
+
     /**
      * 登录验证
      * 
@@ -178,4 +188,45 @@ public class SysLoginService
         sysUser.setLoginDate(DateUtils.getNowDate());
         userService.updateUserProfile(sysUser);
     }
+
+    /**
+     *   手机号验证码登录
+     * @param phone 手机号
+     * @param code  验证码
+     * @param uuid  唯一标识
+     * @return
+     */
+    public String loginByTelephone(String phone, String code, String uuid) {
+
+        //校验验证码
+        // 这里需要对自己的验证码进行校验 我这里就省略了校验
+
+
+
+        Authentication authentication = null; // 用户验证
+        try {
+
+
+            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(phone, Constants.CUSTOM_LOGIN_SMS);
+            AuthenticationContextHolder.setContext(authenticationToken);
+            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
+            authentication = authenticationPhoneManager.authenticate(authenticationToken);
+
+        }
+        catch (Exception e) {
+            if (e instanceof BadCredentialsException) {
+                throw new UserPasswordNotMatchException();          //抛出账号或者密码错误的异常
+            } else {
+                throw new ServiceException(e.getMessage());         //抛出其他异常
+            }
+        } finally {
+            AuthenticationContextHolder.clearContext();
+        }
+        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
+        recordLoginInfo(loginUser.getUserId());                     //修改sys_user最近登录IP和登录时间
+        // 生成token
+        return tokenService.createToken(loginUser);
+
+
+    }
 }

+ 66 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsByPhoneNumberServiceImpl.java

@@ -0,0 +1,66 @@
+package com.ruoyi.framework.web.service;
+
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.enums.UserStatus;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.MessageUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.service.ISysUserService;
+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.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+/**
+ * 用户验证处理
+ *
+ * @author ruoyi
+ */
+@Service("userDetailsByPhoneNumber")
+public class UserDetailsByPhoneNumberServiceImpl implements UserDetailsService
+{
+    private static final Logger log = LoggerFactory.getLogger(UserDetailsByPhoneNumberServiceImpl.class);
+
+    @Autowired
+    private ISysUserService userService;
+    
+    @Autowired
+    private SysPasswordService passwordService;
+
+    @Autowired
+    private SysPermissionService permissionService;
+
+    @Override
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
+    {
+        SysUser user = userService.selectUserByUserName(username);
+        if (StringUtils.isNull(user))
+        {
+            log.info("登录用户:{} 不存在.", username);
+            throw new ServiceException(MessageUtils.message("user.not.exists"));
+        }
+        else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
+        {
+            log.info("登录用户:{} 已被删除.", username);
+            throw new ServiceException(MessageUtils.message("user.password.delete"));
+        }
+        else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
+        {
+            log.info("登录用户:{} 已被停用.", username);
+            throw new ServiceException(MessageUtils.message("user.blocked"));
+        }
+
+        passwordService.validate(user);
+
+        return createLoginUser(user);
+    }
+
+    public UserDetails createLoginUser(SysUser user)
+    {
+        return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
+    }
+}

+ 1 - 1
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java

@@ -20,7 +20,7 @@ import com.ruoyi.system.service.ISysUserService;
  *
  * @author ruoyi
  */
-@Service
+@Service("UserDetailsServiceImpl")
 public class UserDetailsServiceImpl implements UserDetailsService
 {
     private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);