Browse Source

以 tnb-account 的 security 包为模板, 修改 security 包

reghao 2 years ago
parent
commit
1a764db586
21 changed files with 475 additions and 106 deletions
  1. 5 0
      manager/pom.xml
  2. 1 1
      manager/src/main/java/cn/reghao/devops/manager/app/service/bd/impl/BuildAppImpl.java
  3. 2 2
      manager/src/main/java/cn/reghao/devops/manager/home/controller/HomePageController.java
  4. 1 1
      manager/src/main/java/cn/reghao/devops/manager/rbac/controller/MenuController.java
  5. 31 0
      manager/src/main/java/cn/reghao/devops/manager/rbac/model/dto/AccountLoginDto.java
  6. 52 17
      manager/src/main/java/cn/reghao/devops/manager/rbac/security/WebSecurityConfig.java
  7. 36 0
      manager/src/main/java/cn/reghao/devops/manager/rbac/security/encoder/Md5PasswordEncoder.java
  8. 30 0
      manager/src/main/java/cn/reghao/devops/manager/rbac/security/encoder/Sha256PasswordEncoder.java
  9. 5 17
      manager/src/main/java/cn/reghao/devops/manager/rbac/security/filter/LoginRedirectFilter.java
  10. 64 0
      manager/src/main/java/cn/reghao/devops/manager/rbac/security/form/AccountAuthFilter.java
  11. 29 27
      manager/src/main/java/cn/reghao/devops/manager/rbac/security/form/AccountAuthProvider.java
  12. 82 0
      manager/src/main/java/cn/reghao/devops/manager/rbac/security/form/AccountAuthToken.java
  13. 0 36
      manager/src/main/java/cn/reghao/devops/manager/rbac/security/form/UserDetailsServiceImpl.java
  14. 3 1
      manager/src/main/java/cn/reghao/devops/manager/rbac/security/handler/AuthFailHandlerImpl.java
  15. 3 1
      manager/src/main/java/cn/reghao/devops/manager/rbac/security/handler/AuthSuccessHandlerImpl.java
  16. 21 0
      manager/src/main/java/cn/reghao/devops/manager/rbac/security/session/MySessionAuthenticationStrategy.java
  17. 18 0
      manager/src/main/java/cn/reghao/devops/manager/rbac/service/AccountAuthService.java
  18. 86 0
      manager/src/main/java/cn/reghao/devops/manager/rbac/service/impl/AccountAuthServiceImpl.java
  19. 2 1
      manager/src/main/java/cn/reghao/devops/manager/rbac/service/impl/MenuServiceImpl.java
  20. 2 1
      manager/src/main/java/cn/reghao/devops/manager/rbac/service/impl/RoleServiceImpl.java
  21. 2 1
      manager/src/main/java/cn/reghao/devops/manager/rbac/service/impl/UserServiceImpl.java

+ 5 - 0
manager/pom.xml

@@ -34,6 +34,11 @@
     </dependencyManagement>
 
     <dependencies>
+        <dependency>
+            <groupId>cn.reghao.jutil</groupId>
+            <artifactId>web</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
         <dependency>
             <groupId>cn.reghao.devops</groupId>
             <artifactId>common</artifactId>

+ 1 - 1
manager/src/main/java/cn/reghao/devops/manager/app/service/bd/impl/BuildAppImpl.java

@@ -71,7 +71,7 @@ public class BuildAppImpl implements BuildApp {
                 throw new Exception("没有权限");
             }
         }
-        User user = (User) authToken.getPrincipal();
+        User user = (User) authToken.getDetails();
         String buildBy = user.getNickname();
         AppDto appDto = appConfig.getAppDto();
         localBuild(appDto, buildBy, deploy);

+ 2 - 2
manager/src/main/java/cn/reghao/devops/manager/home/controller/HomePageController.java

@@ -40,7 +40,7 @@ public class HomePageController implements ErrorController {
         if (authToken == null) {
             throw new Exception("未登录");
         }
-        User user = (User) authToken.getPrincipal();
+        User user = (User) authToken.getDetails();
 
         List<Menu> menus = homeService.userMenus(user.getRole());
         Map<Integer, Menu> treeMenu = homeService.treeMenu(menus);
@@ -66,7 +66,7 @@ public class HomePageController implements ErrorController {
     @GetMapping("/userInfo")
     public String userInfoPage(Model model) {
         Authentication authToken = SecurityContextHolder.getContext().getAuthentication();
-        User user = (User) authToken.getPrincipal();
+        User user = (User) authToken.getDetails();
         model.addAttribute("user", user);
         return "/home/userInfo";
     }

+ 1 - 1
manager/src/main/java/cn/reghao/devops/manager/rbac/controller/MenuController.java

@@ -4,7 +4,7 @@ import cn.reghao.jutil.jdk.result.Result;
 import cn.reghao.devops.manager.rbac.db.query.MenuQuery;
 import cn.reghao.devops.manager.rbac.model.dto.MenuDTO;
 import cn.reghao.devops.manager.rbac.model.po.Menu;
-import cn.reghao.devops.manager.rbac.service.MenuServiceImpl;
+import cn.reghao.devops.manager.rbac.service.impl.MenuServiceImpl;
 import cn.reghao.jutil.jdk.result.WebBody;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;

+ 31 - 0
manager/src/main/java/cn/reghao/devops/manager/rbac/model/dto/AccountLoginDto.java

@@ -0,0 +1,31 @@
+package cn.reghao.devops.manager.rbac.model.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+/**
+ * 用户登录提交的数据
+ *
+ * @author reghao
+ * @date 2021-11-17 16:36:05
+ */
+@AllArgsConstructor
+@Setter
+@Getter
+public class AccountLoginDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @NotBlank
+    private String username;
+    @NotBlank
+    private String password;
+    private Boolean rememberMe;
+
+    public AccountLoginDto() {
+        this.rememberMe = false;
+    }
+}

+ 52 - 17
manager/src/main/java/cn/reghao/devops/manager/rbac/security/WebSecurityConfig.java

@@ -1,10 +1,11 @@
 package cn.reghao.devops.manager.rbac.security;
 
 import cn.reghao.devops.manager.rbac.security.filter.LoginRedirectFilter;
-import cn.reghao.devops.manager.rbac.security.form.UserDetailsServiceImpl;
+import cn.reghao.devops.manager.rbac.security.form.AccountAuthFilter;
 import cn.reghao.devops.manager.rbac.security.form.AccountAuthProvider;
-import cn.reghao.devops.manager.rbac.security.handler.WebAuthFailureHandlerImpl;
-import cn.reghao.devops.manager.rbac.security.handler.WebAuthSuccessHandlerImpl;
+import cn.reghao.devops.manager.rbac.security.handler.AuthFailHandlerImpl;
+import cn.reghao.devops.manager.rbac.security.handler.AuthSuccessHandlerImpl;
+import cn.reghao.devops.manager.rbac.service.AccountAuthService;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.access.expression.SecurityExpressionHandler;
@@ -18,7 +19,10 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.web.FilterInvocation;
 import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.context.SecurityContextPersistenceFilter;
 
 /**
  * Web 应用安全配置
@@ -30,10 +34,19 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
 @EnableWebSecurity
 @EnableGlobalMethodSecurity(prePostEnabled = true) // 调用方法时检查权限
 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
-    private final UserDetailsServiceImpl userDetailsService;
+    private final String loginApi = "/login";
 
-    public WebSecurityConfig(UserDetailsServiceImpl userDetailsService) {
-        this.userDetailsService = userDetailsService;
+    private final AccountAuthProvider userAuthProvider;
+    private final AccountAuthService accountAuthService;
+    private final AuthenticationSuccessHandler successHandler;
+    private final AuthenticationFailureHandler failureHandler;
+
+    public WebSecurityConfig(AccountAuthProvider userAuthProvider, AccountAuthService accountAuthService,
+                             AuthenticationSuccessHandler successHandler, AuthenticationFailureHandler failureHandler) {
+        this.userAuthProvider = userAuthProvider;
+        this.accountAuthService = accountAuthService;
+        this.successHandler = successHandler;
+        this.failureHandler = failureHandler;
     }
 
     /**
@@ -65,14 +78,12 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .anyRequest().authenticated();
         http.exceptionHandling().accessDeniedPage("/deny");
 
-        // 禁用 CSRF
-        http.csrf().disable();
-
         // 处理 X-Frame-Options header
         http.headers().frameOptions().sameOrigin();
 
         // 在 UsernamePasswordAuthenticationFilter 后添加 filter
-        http.addFilterAfter(new LoginRedirectFilter(), UsernamePasswordAuthenticationFilter.class);
+        http.addFilterAfter(new LoginRedirectFilter(), SecurityContextPersistenceFilter.class);
+        http.addFilterBefore(accountAuthFilter(), UsernamePasswordAuthenticationFilter.class);
 
         // 基于表单的认证
         http.formLogin()
@@ -80,16 +91,27 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .loginPage("/login")
                 // 登录接口(POST 请求)
                 .loginProcessingUrl("/login")
-                .successHandler(new WebAuthSuccessHandlerImpl())
-                .failureHandler(new WebAuthFailureHandlerImpl())
+                .successHandler(new AuthSuccessHandlerImpl())
+                .failureHandler(new AuthFailHandlerImpl())
                 .and()
                 .logout()
                 .logoutSuccessUrl("/")
                 .and()
                 .httpBasic().disable();
 
-        // HTTP Basic 认证
-        http.httpBasic();
+        // 配置 RememberMeAuthenticationFilter, 禁用 RememberMeAuthenticationFilter
+        http.rememberMe().disable();
+                /*.key("DExNzAyNTQ2Nzo3NDI3MTNhYmM5MGE5")
+                .rememberMeParameter("rememberMe");*/
+
+        // 配置 CorsFilter, 禁用 CorsFilter
+        http.cors().disable();
+
+        // 配置 CsrfFilter, 禁用 CsrfFilter
+        http.csrf().disable();
+
+        // 配置 HeaderWriterFilter, 禁用 HeaderWriterFilter
+        http.headers().disable();
     }
 
     /**
@@ -101,9 +123,22 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
      */
     @Override
     public void configure(AuthenticationManagerBuilder auth) throws Exception {
-        //auth.userDetailsService(userDetailsService);
-        // TODO 尝试使用 DaoAuthenticationProvider
-        auth.authenticationProvider(new AccountAuthProvider(userDetailsService));
+        auth.authenticationProvider(userAuthProvider);
+    }
+
+    /**
+     * 配置账号密码登录 filter
+     *
+     * @param
+     * @return
+     * @date 2022-07-06 上午9:54
+     */
+    private AccountAuthFilter accountAuthFilter() throws Exception {
+        AccountAuthFilter filter = new AccountAuthFilter(loginApi, "POST", accountAuthService);
+        filter.setAuthenticationManager(super.authenticationManager());
+        filter.setAuthenticationSuccessHandler(successHandler);
+        filter.setAuthenticationFailureHandler(failureHandler);
+        return filter;
     }
 
     /**

+ 36 - 0
manager/src/main/java/cn/reghao/devops/manager/rbac/security/encoder/Md5PasswordEncoder.java

@@ -0,0 +1,36 @@
+package cn.reghao.devops.manager.rbac.security.encoder;
+
+import cn.reghao.jutil.jdk.security.Md5Cryptor;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Component;
+
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @author reghao
+ * @date 2019-03-26 14:46:57
+ */
+@Component
+public class Md5PasswordEncoder implements PasswordEncoder {
+    private final Md5Cryptor md5Cryptor;
+
+    public Md5PasswordEncoder() throws NoSuchAlgorithmException {
+        this.md5Cryptor = new Md5Cryptor();
+    }
+
+    @Override
+    public String encode(CharSequence rawPassword) {
+        // rawPassword 带盐值
+        return md5Cryptor.encrypt(rawPassword.toString());
+    }
+
+    private byte[] encode(CharSequence rawPassword, byte[] salt) {
+        return null;
+    }
+
+    @Override
+    public boolean matches(CharSequence rawPassword, String encodedPassword) {
+        String password = md5Cryptor.encrypt(rawPassword.toString());
+        return encodedPassword.equals(password);
+    }
+}

+ 30 - 0
manager/src/main/java/cn/reghao/devops/manager/rbac/security/encoder/Sha256PasswordEncoder.java

@@ -0,0 +1,30 @@
+package cn.reghao.devops.manager.rbac.security.encoder;
+
+import cn.reghao.jutil.jdk.security.Sha256Cryptor;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @author reghao
+ * @date 2019-03-26 14:46:57
+ */
+public class Sha256PasswordEncoder implements PasswordEncoder {
+    private final Sha256Cryptor sha256Cryptor;
+
+    public Sha256PasswordEncoder() throws NoSuchAlgorithmException {
+        this.sha256Cryptor = new Sha256Cryptor();
+    }
+
+    @Override
+    public String encode(CharSequence rawPassword) {
+        // rawPassword 带盐值
+        return sha256Cryptor.encrypt(rawPassword.toString());
+    }
+
+    @Override
+    public boolean matches(CharSequence rawPassword, String encodedPassword) {
+        String password = sha256Cryptor.encrypt(rawPassword.toString());
+        return encodedPassword.equals(password);
+    }
+}

+ 5 - 17
manager/src/main/java/cn/reghao/devops/manager/rbac/security/filter/LoginRedirectFilter.java

@@ -1,14 +1,11 @@
 package cn.reghao.devops.manager.rbac.security.filter;
 
-import cn.reghao.devops.manager.rbac.UserContext;
-import cn.reghao.devops.manager.rbac.model.po.User;
-import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
 
 import javax.servlet.*;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
 import java.io.IOException;
 
 /**
@@ -23,21 +20,12 @@ public class LoginRedirectFilter implements Filter {
             throws IOException, ServletException {
         HttpServletRequest httpRequest = (HttpServletRequest) request;
         HttpServletResponse httpResponse = (HttpServletResponse) response;
-        HttpSession session = httpRequest.getSession();
 
+        SecurityContext securityContext = SecurityContextHolder.getContext();
         String url = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
-        SecurityContext securityContext = (SecurityContext) session.getAttribute("SPRING_SECURITY_CONTEXT");
-        if (securityContext != null) {
-            Authentication authentication = securityContext.getAuthentication();
-            User user = (User) authentication.getPrincipal();
-            try (UserContext context = new UserContext(user)) {
-                if ("/login".equals(url)) {
-                    // 将登录页面重定向到主页
-                    httpResponse.sendRedirect("/");
-                } else {
-                    chain.doFilter(request, response);
-                }
-            }
+        if (securityContext != null && securityContext.getAuthentication() != null && "/login".equals(url)) {
+            // 将登录页面重定向到主页
+            httpResponse.sendRedirect("/");
         } else {
             chain.doFilter(request, response);
         }

+ 64 - 0
manager/src/main/java/cn/reghao/devops/manager/rbac/security/form/AccountAuthFilter.java

@@ -0,0 +1,64 @@
+package cn.reghao.devops.manager.rbac.security.form;
+
+import cn.reghao.devops.manager.rbac.model.dto.AccountLoginDto;
+import cn.reghao.devops.manager.rbac.service.AccountAuthService;
+import cn.reghao.jutil.web.ServletUtil;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 账号密码登录 filter
+ * 替换 UsernamePasswordAuthenticationFilter,用于认证用户,匹配指定 URL 才会进行处理
+ *
+ * @author reghao
+ * @date 2022-07-06 15:55:59
+ */
+public class AccountAuthFilter extends AbstractAuthenticationProcessingFilter {
+    private final AccountAuthService accountAuthService;
+
+    public AccountAuthFilter(String authUrl, String httpMethod, AccountAuthService accountAuthService) {
+        super(new AntPathRequestMatcher(authUrl, httpMethod));
+        this.accountAuthService = accountAuthService;
+    }
+
+    /**
+     * 构造 Authentication 对象
+     * 参照 UsernamePasswordAuthenticationFilter
+     *
+     * @param
+     * @return
+     * @date 2020-05-06 上午11:16
+     */
+    @Override
+    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
+            throws AuthenticationException, IOException, ServletException {
+        // form-data 中的 username 和 password 参数
+        String body = ServletUtil.getBody();
+        Map<String, String> map = new HashMap<>();
+        String[] arr = body.split("&");
+        for (String ele : arr) {
+            String[] param = ele.split("=");
+            map.put(param[0], param[1]);
+        }
+
+        String username = map.get("username");
+        String password = map.get("password");
+        String rememberMeStr = map.get("rememberMe");
+        boolean rememberMe = false;
+
+        //AccountLoginDto userLoginDto = (AccountLoginDto) ServletUtil.getBody(request, AccountLoginDto.class);
+        AccountLoginDto userLoginDto = new AccountLoginDto(username, password, rememberMe);
+        AccountAuthToken preAuthToken = accountAuthService.getPreAuthentication(userLoginDto);
+        // 调用 UserAuthProvider.authenticate()
+        return this.getAuthenticationManager().authenticate(preAuthToken);
+    }
+}

+ 29 - 27
manager/src/main/java/cn/reghao/devops/manager/rbac/security/form/AccountAuthProvider.java

@@ -1,15 +1,11 @@
 package cn.reghao.devops.manager.rbac.security.form;
 
-import cn.reghao.devops.manager.rbac.security.exceptioin.AccountLoginException;
-import cn.reghao.jutil.jdk.security.Cryptor;
-import cn.reghao.jutil.jdk.security.Md5Cryptor;
-import cn.reghao.devops.manager.rbac.model.po.User;
+import cn.reghao.devops.manager.rbac.service.AccountAuthService;
 import org.springframework.security.authentication.AuthenticationProvider;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.authentication.DisabledException;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
-
-import java.security.NoSuchAlgorithmException;
+import org.springframework.stereotype.Component;
 
 /**
  * 认证用户名/密码登录的 provider
@@ -17,35 +13,41 @@ import java.security.NoSuchAlgorithmException;
  * @author reghao
  * @date 2019-04-09 09:07:40
  */
+@Component
 public class AccountAuthProvider implements AuthenticationProvider {
-    private final UserDetailsServiceImpl userDetailsService;
-    private final Cryptor cryptor;
+    private final AccountAuthService accountAuthService;
 
-    public AccountAuthProvider(UserDetailsServiceImpl userDetailsService)
-            throws NoSuchAlgorithmException {
-        this.userDetailsService = userDetailsService;
-        this.cryptor = new Md5Cryptor();
+    public AccountAuthProvider(AccountAuthService accountAuthService) {
+        this.accountAuthService = accountAuthService;
     }
 
+    /**
+     * 对 Authentication 对象进行认证
+     * 参照 DaoAuthenticationProvider
+     *
+     * @param
+     * @return
+     * @date 2020-05-06 上午11:16
+     */
     @Override
     public Authentication authenticate(Authentication authentication) throws AuthenticationException {
-        String username = authentication.getName();
-        String password = (String) authentication.getCredentials();
-
-        // TODO 缓存中查找
-        // 从数据库中获取用户认证信息
-        User user = (User) userDetailsService.loadUserByUsername(username);
-        String encryptedPwd = cryptor.encrypt(password + user.getSalt());
-        if (!user.getPassword().equals(encryptedPwd)) {
-            String errMsg = "账号或密码不正确";
-            throw new AccountLoginException(errMsg);
-        }
-
-        return new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
+        AccountAuthToken preAuthToken = (AccountAuthToken) authentication;
+        AccountAuthToken authToken = accountAuthService.authByPassword(preAuthToken);
+
+        /*int loginType = preAuthToken.getLoginType();
+        if (loginType == LoginType.mobileCode.getValue()) {
+            authToken = accountAuthService.authByVerifyCode(preAuthToken);
+        } else if (loginType == LoginType.usernamePassword.getValue()) {
+        } else if (loginType == LoginType.thirdParty.getValue()) {
+            authToken = accountAuthService.authByThirdParty(preAuthToken);
+        } else {
+            throw new DisabledException("登录类型错误");
+        }*/
+        return authToken;
     }
 
     @Override
     public boolean supports(Class<?> authentication) {
-        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
+        return (AccountAuthToken.class.isAssignableFrom(authentication));
     }
 }

+ 82 - 0
manager/src/main/java/cn/reghao/devops/manager/rbac/security/form/AccountAuthToken.java

@@ -0,0 +1,82 @@
+package cn.reghao.devops.manager.rbac.security.form;
+
+import cn.reghao.devops.manager.rbac.model.po.User;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.SpringSecurityCoreVersion;
+
+/**
+ * 参考 org.springframework.security.authentication.UsernamePasswordAuthenticationToken 实现
+ *
+ * @author reghao
+ * @date 2022-07-06 15:59:02
+ */
+public class AccountAuthToken extends AbstractAuthenticationToken {
+    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
+
+    // ~ Instance fields
+    // ================================================================================================
+    private final boolean rememberMe;
+    private final Object principal;
+    private Object credentials;
+
+    // ~ Constructors
+    // ===================================================================================================
+
+    /**
+     * 等待认证时创建的 Authentication 对象
+     *
+     * This constructor can be safely used by any code that wishes to create a
+     * <code>PasswordAuthToken</code>, as the {@link #isAuthenticated()}
+     * will return <code>false</code>.
+     *
+     */
+    public AccountAuthToken(Boolean rememberMe, Object principal, Object credentials) {
+        super(null);
+        super.setAuthenticated(false);
+        this.rememberMe = rememberMe;
+        this.principal = principal;
+        this.credentials = credentials;
+    }
+
+    /**
+     * 认证通过时创建的 Authentication 对象
+     *
+     * This constructor should only be used by <code>AuthenticationManager</code> or
+     * <code>AuthenticationProvider</code> implementations that are satisfied with
+     * producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)
+     * authentication token.
+     *
+     * @param authToken
+     * @param user
+     */
+    public AccountAuthToken(AccountAuthToken authToken, User user) {
+        super(user.getAuthorities());
+        super.setAuthenticated(true); // must use super, as we override
+        super.setDetails(user);
+        this.rememberMe = authToken.isRememberMe();
+        this.principal = authToken.getPrincipal();
+        this.credentials = authToken.getCredentials();
+    }
+
+    // ~ Methods
+    // ========================================================================================================
+    public boolean isRememberMe() {
+        return rememberMe;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return this.credentials;
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return this.principal;
+    }
+
+    @Override
+    public void eraseCredentials() {
+        super.eraseCredentials();
+        credentials = null;
+    }
+}

+ 0 - 36
manager/src/main/java/cn/reghao/devops/manager/rbac/security/form/UserDetailsServiceImpl.java

@@ -1,36 +0,0 @@
-package cn.reghao.devops.manager.rbac.security.form;
-
-import cn.reghao.devops.manager.rbac.model.po.User;
-import cn.reghao.devops.manager.rbac.service.UserService;
-import org.springframework.security.authentication.DisabledException;
-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 reghao
- * @date 2019-05-20 11:03:56
- */
-@Service
-public class UserDetailsServiceImpl implements UserDetailsService {
-    private UserService userService;
-
-    public UserDetailsServiceImpl(UserService userService) {
-        this.userService = userService;
-    }
-
-    @Override
-    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
-        User user = userService.getUser(username);
-        if (user != null) {
-            return user;
-        } else {
-            // Spring Security 会将 UsernameNotFoundException 捕获并替换,使得前端无法看到信息
-            //throw new UsernameNotFoundException(email + " 未注册");
-            throw new DisabledException(username + " 未注册");
-        }
-    }
-}

+ 3 - 1
manager/src/main/java/cn/reghao/devops/manager/rbac/security/handler/WebAuthFailureHandlerImpl.java → manager/src/main/java/cn/reghao/devops/manager/rbac/security/handler/AuthFailHandlerImpl.java

@@ -3,6 +3,7 @@ package cn.reghao.devops.manager.rbac.security.handler;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import org.springframework.stereotype.Component;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
@@ -15,7 +16,8 @@ import java.io.IOException;
  * @author reghao
  * @date 2019-04-09 00:02:34
  */
-public class WebAuthFailureHandlerImpl implements AuthenticationFailureHandler {
+@Component
+public class AuthFailHandlerImpl implements AuthenticationFailureHandler {
     @Override
     public void onAuthenticationFailure(HttpServletRequest request,
                                         HttpServletResponse response,

+ 3 - 1
manager/src/main/java/cn/reghao/devops/manager/rbac/security/handler/WebAuthSuccessHandlerImpl.java → manager/src/main/java/cn/reghao/devops/manager/rbac/security/handler/AuthSuccessHandlerImpl.java

@@ -4,6 +4,7 @@ import cn.reghao.jutil.jdk.result.WebBody;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
 import org.springframework.security.web.savedrequest.SavedRequest;
+import org.springframework.stereotype.Component;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -16,7 +17,8 @@ import java.io.PrintWriter;
  * @author reghao
  * @date 2019-04-08 23:50:51
  */
-public class WebAuthSuccessHandlerImpl implements AuthenticationSuccessHandler {
+@Component
+public class AuthSuccessHandlerImpl implements AuthenticationSuccessHandler {
     @Override
     public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth)
             throws IOException {

+ 21 - 0
manager/src/main/java/cn/reghao/devops/manager/rbac/security/session/MySessionAuthenticationStrategy.java

@@ -0,0 +1,21 @@
+package cn.reghao.devops.manager.rbac.security.session;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.session.SessionRegistry;
+import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 配置 SessionManagementFilter 中的 SessionAuthenticationStrategy
+ *
+ * @author reghao
+ * @date 2023-08-09 16:19:50
+ */
+public class MySessionAuthenticationStrategy implements SessionAuthenticationStrategy {
+    private SessionRegistry sessionRegistry;
+
+    public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
+    }
+}

+ 18 - 0
manager/src/main/java/cn/reghao/devops/manager/rbac/service/AccountAuthService.java

@@ -0,0 +1,18 @@
+package cn.reghao.devops.manager.rbac.service;
+
+import cn.reghao.devops.manager.rbac.model.dto.AccountLoginDto;
+import cn.reghao.devops.manager.rbac.security.form.AccountAuthToken;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.validation.annotation.Validated;
+
+/**
+ * @author reghao
+ * @date 2021-11-14 14:51:17
+ */
+public interface AccountAuthService extends UserDetailsService {
+    AccountAuthToken getPreAuthentication(@Validated AccountLoginDto userLoginDto) throws AuthenticationException;
+    AccountAuthToken authByPassword(AccountAuthToken authToken);
+    AccountAuthToken authByVerifyCode(AccountAuthToken authToken);
+    AccountAuthToken authByThirdParty(AccountAuthToken authToken);
+}

+ 86 - 0
manager/src/main/java/cn/reghao/devops/manager/rbac/service/impl/AccountAuthServiceImpl.java

@@ -0,0 +1,86 @@
+package cn.reghao.devops.manager.rbac.service.impl;
+
+import cn.reghao.devops.manager.rbac.model.dto.AccountLoginDto;
+import cn.reghao.devops.manager.rbac.model.po.User;
+import cn.reghao.devops.manager.rbac.security.exceptioin.AccountLoginException;
+import cn.reghao.devops.manager.rbac.security.form.AccountAuthToken;
+import cn.reghao.devops.manager.rbac.service.AccountAuthService;
+import cn.reghao.devops.manager.rbac.service.UserService;
+import org.springframework.security.authentication.DisabledException;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author reghao
+ * @date 2022-07-08 14:51:17
+ */
+@Service
+public class AccountAuthServiceImpl implements AccountAuthService {
+    private final UserService userService;
+    private final PasswordEncoder passwordEncoder;
+
+    public AccountAuthServiceImpl(UserService userService, PasswordEncoder passwordEncoder) {
+        this.userService = userService;
+        this.passwordEncoder = passwordEncoder;
+    }
+
+    @Override
+    public AccountAuthToken getPreAuthentication(AccountLoginDto userLoginDto) {
+        String principal = userLoginDto.getUsername();
+        String credential = userLoginDto.getPassword();
+        Boolean rememberMe = userLoginDto.getRememberMe();
+        return new AccountAuthToken(rememberMe, principal, credential);
+    }
+
+    @Override
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+        User user = userService.getUser(username);
+        if (user != null) {
+            return user;
+        } else {
+            // Spring Security 会将 UsernameNotFoundException 捕获并替换,使得前端无法看到信息
+            //throw new UsernameNotFoundException(email + " 未注册");
+            throw new DisabledException(username + " 未注册");
+        }
+    }
+
+    @Override
+    public AccountAuthToken authByPassword(AccountAuthToken authToken) {
+        String username = (String) authToken.getPrincipal();
+        User userDetail = (User) loadUserByUsername(username);
+        if (userDetail == null) {
+            String errMsg = String.format("帐号 %s 未注册", username);
+            throw new UsernameNotFoundException(errMsg);
+        }
+
+        String password = (String) authToken.getCredentials();
+        String encodedPassword = passwordEncoder.encode(password + userDetail.getSalt());
+        if (!userDetail.getPassword().equals(encodedPassword)) {
+            String errMsg = "账号或密码不正确";
+            throw new AccountLoginException(errMsg);
+        }
+
+        return new AccountAuthToken(authToken, userDetail);
+    }
+
+    /**
+     * TODO 手机号验证码登录, 手机号不存在则自动注册
+     *
+     * @param
+     * @return
+     * @date 2022-07-08 下午3:11
+     */
+    @Override
+    public AccountAuthToken authByVerifyCode(AccountAuthToken authToken) {
+        String errMsg = "接口未实现";
+        throw new DisabledException(errMsg);
+    }
+
+    @Override
+    public AccountAuthToken authByThirdParty(AccountAuthToken authToken) {
+        String errMsg = "接口未实现";
+        throw new DisabledException(errMsg);
+    }
+}

+ 2 - 1
manager/src/main/java/cn/reghao/devops/manager/rbac/service/MenuServiceImpl.java → manager/src/main/java/cn/reghao/devops/manager/rbac/service/impl/MenuServiceImpl.java

@@ -1,8 +1,9 @@
-package cn.reghao.devops.manager.rbac.service;
+package cn.reghao.devops.manager.rbac.service.impl;
 
 import cn.reghao.devops.manager.rbac.db.repository.MenuRepository;
 import cn.reghao.devops.manager.rbac.db.repository.RoleRepository;
 import cn.reghao.devops.manager.rbac.model.constant.MenuType;
+import cn.reghao.devops.manager.rbac.service.MenuService;
 import cn.reghao.jutil.jdk.result.Result;
 import cn.reghao.jutil.jdk.result.ResultStatus;
 import cn.reghao.devops.manager.rbac.model.dto.MenuDTO;

+ 2 - 1
manager/src/main/java/cn/reghao/devops/manager/rbac/service/RoleServiceImpl.java → manager/src/main/java/cn/reghao/devops/manager/rbac/service/impl/RoleServiceImpl.java

@@ -1,10 +1,11 @@
-package cn.reghao.devops.manager.rbac.service;
+package cn.reghao.devops.manager.rbac.service.impl;
 
 import cn.reghao.devops.manager.rbac.db.query.RoleQuery;
 import cn.reghao.devops.manager.rbac.db.repository.RoleRepository;
 import cn.reghao.devops.manager.rbac.model.po.Menu;
 import cn.reghao.devops.manager.rbac.model.po.Role;
 import cn.reghao.devops.manager.rbac.model.po.User;
+import cn.reghao.devops.manager.rbac.service.RoleService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 

+ 2 - 1
manager/src/main/java/cn/reghao/devops/manager/rbac/service/UserServiceImpl.java → manager/src/main/java/cn/reghao/devops/manager/rbac/service/impl/UserServiceImpl.java

@@ -1,7 +1,8 @@
-package cn.reghao.devops.manager.rbac.service;
+package cn.reghao.devops.manager.rbac.service.impl;
 
 import cn.reghao.devops.manager.rbac.db.repository.UserRepository;
 import cn.reghao.devops.manager.rbac.model.po.Role;
+import cn.reghao.devops.manager.rbac.service.UserService;
 import cn.reghao.jutil.jdk.security.Cryptor;
 import cn.reghao.jutil.jdk.security.Md5Cryptor;
 import cn.reghao.jutil.jdk.security.RandomString;