admin
2025-05-09 6159dc58f50d3e4680779b7989bbd4d49a76bad5
src/main/java/com/taoke/autopay/config/WebSecurityConfig.java
@@ -1,29 +1,97 @@
package com.taoke.autopay.config;
import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import com.taoke.autopay.entity.AdminUser;
import com.taoke.autopay.exception.VerificationCodeException;
import com.taoke.autopay.service.AdminUserService;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
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.security.web.AuthenticationEntryPoint;
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.authentication.logout.LogoutSuccessHandler;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import org.yeshi.utils.JsonUtil;
import org.yeshi.utils.StringUtil;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Properties;
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private AdminUserService adminUserService;
    private final String LOGIN_PROCESSING_URL = "/admin/api/login";
    private final String LOGIN_PAGE_PATH = "/admin/login.html";
    private final static String[] STATIC_RESOURCE_PATHS = new String[]{
            "/**/*.html", "/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg", "/**/*.gif", "/**/*.xml", "/**/font/*", "/**/fonts/*"
            "/*.html", "/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg", "/**/*.gif", "/**/*.xml", "/**/font/*", "/**/fonts/*", "/**/layui/**"
    };
    /**
     * @return com.google.code.kaptcha.Producer
     * @author hxh
     * @description 图形验证码配置
     * @date 0:29 2024/6/29
     **/
    @Bean
    public Producer captcha() {
        Properties properties = new Properties();
        //图片的宽高
        properties.setProperty("kaptcha.image.width", "150");
        properties.setProperty("kaptcha.image.height", "50");
        //字符集
        properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        //字符长度
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        String color = "47,144,242";
        //边框颜色
        properties.setProperty("kaptcha.border.color", color);
        //字体颜色
        properties.setProperty("kaptcha.textproducer.font.color", color);
        //干扰颜色
        properties.setProperty("kaptcha.noise.color", color);
        Config config = new Config(properties);
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
    private Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class);
@@ -58,7 +126,69 @@
        @Override
        protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
            if (!LOGIN_PROCESSING_URL.equalsIgnoreCase(httpServletRequest.getRequestURI())) {
                //非登录接口
                String url = httpServletRequest.getRequestURI();
                //不验证静态资源
                AntPathMatcher pathMatcher = new AntPathMatcher();
                for (String resource : STATIC_RESOURCE_PATHS) {
                    if (pathMatcher.match(resource, url)) {
                        filterChain.doFilter(httpServletRequest, httpServletResponse);
                        return;
                    }
                }
                //尚未登录授权的就不需要验证权限了
                SecurityContext contextSession = (SecurityContext) httpServletRequest.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
                if (contextSession == null) {
                    filterChain.doFilter(httpServletRequest, httpServletResponse);
                    return;
                }
                Authentication authentication = contextSession.getAuthentication();
                if (authentication == null) {
                    filterChain.doFilter(httpServletRequest, httpServletResponse);
                    return;
                }
                AdminUser adminUser = (AdminUser) authentication.getPrincipal();
                if (adminUser == null) {
                    filterChain.doFilter(httpServletRequest, httpServletResponse);
                    return;
                }
                //验证权限
                filterChain.doFilter(httpServletRequest, httpServletResponse);
                //无权限
//                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, null);
            } else {
                //登录接口
                try {
                    verificationCode(httpServletRequest);
                    filterChain.doFilter(httpServletRequest, httpServletResponse);
                } catch (VerificationCodeException e) {
                    verifyCodeFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);
                }
            }
        }
    }
    /**
     * @return void
     * @author hxh
     * @description 验证验证码是否正确
     * @date 16:54 2022/5/19
     * @param: httpServletRequest
     **/
    private void verificationCode(HttpServletRequest httpServletRequest) throws VerificationCodeException {
        String requestCode = httpServletRequest.getParameter("code");
        HttpSession httpSession = httpServletRequest.getSession();
        String captcha = httpSession.getAttribute("captcha") + "";
        httpSession.removeAttribute("captcha");
        if (StringUtil.isNullOrEmpty(captcha) || StringUtil.isNullOrEmpty(requestCode) || !captcha.equalsIgnoreCase(requestCode)) {
            throw new VerificationCodeException("图形验证码错误");
        }
    }
@@ -67,11 +197,135 @@
    protected void configure(HttpSecurity http) throws Exception {
        http.headers().frameOptions().disable();
        http.authorizeRequests()
                .antMatchers("*/**").permitAll().and().csrf().disable();
       // http.addFilterBefore(new PreRequestVerifyFilter(), UsernamePasswordAuthenticationFilter.class);
                // 配置不需要鉴权的接口
                .antMatchers("/admin/api/captcha.jpg*", "/api/**", "/webapi/**","/credit/api/**","*/agentapi/**").permitAll()
                //配置需要鉴权的接口
                .antMatchers("/admin/api/**", "/admin/index.html").authenticated()
                .and()
                .formLogin()
                //自定义登录界面
                .loginPage(LOGIN_PAGE_PATH)
                //处理登录逻辑的url
                .loginProcessingUrl(LOGIN_PROCESSING_URL)
                //登录成功后的跳转
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        logger.info("successHandler");
                        httpServletResponse.setContentType("application/json;charset=UTF-8");
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("code", 0);
                        jsonObject.put("msg", "登录成功");
                        httpServletResponse.getWriter().print(jsonObject);
                    }
                })
                //登录失败后的处理
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                        logger.info("failureHandler");
                        httpServletResponse.setContentType("application/json;charset=UTF-8");
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("code", 1);
                        jsonObject.put("msg", e.getMessage());
                        httpServletResponse.getWriter().print(jsonObject);
                    }
                })
                .permitAll()
                .and()
                //退出登录
                .logout().logoutUrl("/admin/api/logout").logoutSuccessHandler(new LogoutSuccessHandler() {
            @Override
            public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                logger.info("onLogoutSuccess");
                httpServletResponse.getWriter().print(JsonUtil.loadTrueResult(""));
            }
        })
                .and()
                .csrf().disable()
                .rememberMe().userDetailsService(new MyUserDetailsService())
                .and().exceptionHandling().authenticationEntryPoint(new NotLoginAuthenticationEntryPoint());
        http.addFilterBefore(new PreRequestVerifyFilter(), UsernamePasswordAuthenticationFilter.class);
        // http.addFilterBefore(new PreRequestVerifyFilter(), UsernamePasswordAuthenticationFilter.class);
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(new MyAuthenticationProvider(new MyUserDetailsService(), new PasswordEncoder() {
            @Override
            public String encode(CharSequence charSequence) {
                return charSequence.toString();
            }
            @Override
            public boolean matches(CharSequence charSequence, String s) {
                return s.equalsIgnoreCase(charSequence.toString());
            }
        }));
    }
    class MyUserDetailsService implements UserDetailsService {
        @Override
        public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
            AdminUser adminUser = null;
            if (!StringUtil.isNullOrEmpty(s)) {
                adminUser = adminUserService.selectByAccount(s);
            } else {
                throw new RuntimeException("请填写用户名");
            }
            if (null != adminUser) {
                return adminUser;
            } else {
                throw new RuntimeException("该用户不存在");
            }
        }
    }
    class MyAuthenticationProvider extends DaoAuthenticationProvider {
        public MyAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
            this.setUserDetailsService(userDetailsService);
            this.setPasswordEncoder(passwordEncoder);
        }
        @Override
        protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
            if (usernamePasswordAuthenticationToken.getCredentials() == null) {
                throw new BadCredentialsException("密码不能为空");
            }
            String pwd = StringUtil.Md5(usernamePasswordAuthenticationToken.getCredentials().toString());
            if (!pwd.equalsIgnoreCase(userDetails.getPassword())) {
                throw new BadCredentialsException("密码错误");
            }
        }
    }
    /**
     * @author hxh
     * @description 未登录的事件处理
     * @date 16:55 2022/5/19
     * @return
     **/
    class NotLoginAuthenticationEntryPoint implements AuthenticationEntryPoint {
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response,
                             AuthenticationException authException) throws IOException, ServletException {
            String url = request.getRequestURI();
            if (url.contains("/admin/api/")) {
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
            } else {
                response.sendRedirect(LOGIN_PAGE_PATH);
            }
        }
    }
}