package com.everyday.word.config; import com.everyday.word.entity.AdminUser; import com.everyday.word.exception.VerificationCodeException; import com.everyday.word.service.AdminUserService; import com.google.code.kaptcha.Producer; import com.google.code.kaptcha.impl.DefaultKaptcha; import com.google.code.kaptcha.util.Config; 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/*", "/**/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); /** * @author hxh * @description 请求之前的验证器 * @date 16:51 2022/5/19 * @return **/ class PreRequestVerifyFilter extends OncePerRequestFilter { //处理验证码出错 private AuthenticationFailureHandler verifyCodeFailureHandler = new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { httpServletResponse.setContentType("application/json;charset=UTF-8"); JSONObject jsonObject = new JSONObject(); jsonObject.put("code", 11); jsonObject.put("msg", "验证码错误"); httpServletResponse.getWriter().print(jsonObject); } }; //处理没有权限 private AuthenticationFailureHandler authenticationFailureHandler = new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value()); } }; @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("图形验证码错误"); } } @Override protected void configure(HttpSecurity http) throws Exception { http.headers().frameOptions().disable(); http.authorizeRequests() // 配置不需要鉴权的接口 .antMatchers("/admin/api/captcha.jpg*", "/api/**", "/webapi/**","*/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); } } } }