SpringBoot + Spring Security多种登录方式:账号+微信网页授权登录

2023-11-17

大家好,我是宝哥!

一、概述

实现账号用户名+微信网页授权登录集成在Spring Security的思路,最重要的一点是要实现微信登录通过Spring Security安全框架时,不需要验证账号、密码。

二、准备工作

要实现该功能,首先需要掌握Spring Security框架和微信扫码登录接口相关技术,如果对这两块还不太熟悉,可以参考:

1、Springboot + Spring Security实现前后端分离登录认证及权限控制

  • https://blog.csdn.net/xue317378914/article/details/115318318

2、微信开放平台开发第三方授权登陆:微信扫码登录

  • https://blog.csdn.net/xue317378914/article/details/115318810

三、项目代码结构

a909efffe2a434c8567f6526985db8ad.png

四、Spring Security核心配置:WebSecurityConfig

在WebSecurityConfig中配置了用户名密码登陆的验证以及token授权登陆两种方式,并分别通过不同的拦截器和不同的验证方式来实现该功能。

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
  @Autowired MyAuthenticationnSuccessHandler myAuthenticationSuccessHandler;
  @Autowired MyAuthenticationFailureHandler myAuthenticationFailureHandler;
  @Autowired
  WxAuthenticationnSuccessHandler wxAuthenticationnSuccessHandler;
  @Autowired
  WxAuthenticationFailureHandler wxAuthenticationFailureHandler;
  @Autowired private DataSource dataSource;
  @Autowired
  RedisOneNetUtil redisOneNetUtil;
  @Value("${companyLog.loginPage}")
  private String loginPage;

  @Bean
  public JdbcTokenRepositoryImpl tokenRepository() {
    JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
    tokenRepository.setDataSource(dataSource);
    // tokenRepository.setCreateTableOnStartup(true); // 启动创建表,创建成功后注释掉
    return tokenRepository;
  }
  @Bean
  UserDetailsService customUserService() { // 注册UserDetailsService 的bean
    return new CustomUserServiceImpl();
  }
  @Bean
  UserDetailsService weChatUserService() { // 注册UserDetailsService 的bean
    return new WeChatUserServiceImpl();
  }
  /**
   * 此处给AuthenticationManager添加登陆验证的逻辑。
   * 这里添加了两个AuthenticationProvider分别用于用户名密码登陆的验证以及token授权登陆两种方式。
   * 在处理登陆信息的过滤器执行的时候会调用这两个provider进行登陆验证。
   */
  @Override
  public void configure(AuthenticationManagerBuilder auth) throws Exception {
    //用户名和密码登陆
    auth.userDetailsService(customUserService()).passwordEncoder(new BCryptPasswordEncoder());
    //微信openid登陆
    auth.authenticationProvider(weChatAuthenticationProvider());
  }

  //用户名和密码登陆处理
  /*@Bean
  public CustomAuthenticationProvider customAuthenticationProvider() {
    return new CustomAuthenticationProvider();
  }
*/
  //微信openid登陆处理
  @Bean
  public WeChatAuthenticationProvider weChatAuthenticationProvider() {
    return new WeChatAuthenticationProvider();
  }

  /**
   * 添加微信openid登陆验证的过滤器
   */
  @Bean
  public WeChatAuthenticationFilter weChatAuthenticationFilter() throws Exception {
    WeChatAuthenticationFilter filter = new WeChatAuthenticationFilter();
    filter.setAuthenticationManager(authenticationManagerBean());
    filter.setAuthenticationSuccessHandler(wxAuthenticationnSuccessHandler);
    filter.setAuthenticationFailureHandler(wxAuthenticationFailureHandler);
    return filter;
  }

  /**
   * 添加用户名和密码登陆验证的过滤器
   */
  @Bean
  public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
    CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
    filter.setAuthenticationManager(authenticationManagerBean());
    filter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
    filter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
    return filter;
  }

  /** 配置请求拦截 */
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // http: // 192.168.1.225:8080/users/restPwdView?userid=6&taskcode=8grf3B
    HttpMethodFilter filter = new HttpMethodFilter();
    WeChatAuthenticationFilter wechatFilter = weChatAuthenticationFilter();
    CustomAuthenticationFilter customFilter = customAuthenticationFilter();
    ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
    validateCodeFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
    // http.httpBasic()    //httpBasic登录 BasicAuthenticationFilter
    // 必须在注册之后的过滤器之间才能安插过滤器
    http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
            .addFilterBefore(wechatFilter, UsernamePasswordAuthenticationFilter.class)
            .addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class)
        .addFilterAfter(validateCodeFilter, HttpMethodFilter.class)
        //表单登录,loginPage为登录请求的url,loginProcessingUrl为表单登录处理的URL
        .formLogin()
        .loginPage(loginPage)
        // 登录需要经过的url请求
        .loginProcessingUrl("/user/login")
        .loginProcessingUrl("/wechat/weChatLogin")
        //.successHandler(myAuthenticationSuccessHandler)
        //.failureHandler(myAuthenticationFailureHandler)
        .and()
        .authorizeRequests()
        .antMatchers(
            loginPage,
            "/comMonAssessScreens",
            "/comMonAssessScreen",
            "/alarmConfiguration/ifCheck",
            "/logOut",
            "/code/image",
            "/meterData/insertElecMeterDataList",
            "/meterDataCreate/*",
            "/common/**",
            "/common/js/**",
            
            "/wechat/login",
            "/wechat/weChatLogin_epf",
            "/wechat/userLogin",
            "/wechat/userBindLogin",
            "/wechat/userBindGo",
            "/wechat/userBind",
            "/wechat/userUnBind",
            "/weChatLogin",
            "/weChatLogin.html",
            "/indexV2")
        .permitAll()
        .antMatchers("/static/**")
        .permitAll() // 不拦截静态资源
        .antMatchers("/views/**")
        .permitAll() // 不拦截静态资源
        .antMatchers("/script/**")
        .hasAuthority("ROLE_SuperPermission")
        .antMatchers("/**")
        .fullyAuthenticated()
        // 需要身份认证
        .and()
        // 登出后根据用户读取登出页面
        .logout()
        .logoutUrl("/logOut") // 配置登出请求路径
        .invalidateHttpSession(true)
        .and()
        .headers()
        .frameOptions()
        .sameOrigin()
        .and()
        .rememberMe()
        .tokenRepository(tokenRepository())
        .tokenValiditySeconds(3600) // Token过期时间为一个小时
        .and()
        .csrf()
        .disable() // 注销行为任意访问
        .headers()
        // 增加csp防xss攻击    frame-ancestors 针对frame的加载策略     default-src 针对默认加载策略 object-src 针对插件的加载策略
        .contentSecurityPolicy(
            "frame-ancestors 'self'; default-src 'self' 'unsafe-inline' 'unsafe-eval' *.aliyuncs.com *.baidu.com *.bdimg.com ;object-src 'self'");
  }
}

1、configure分别配置两种登录验证方式,用户名和密码登陆使用userDetailsService方法返回的是带有用户名和密码的token,而authenticationProvider方法返回的是含有微信openid的自定义token,分别根据自己的验证逻辑来实现登录验证。

public void configure(AuthenticationManagerBuilder auth) throws Exception {
    //用户名和密码登陆
    auth.userDetailsService(customUserService()).passwordEncoder(new BCryptPasswordEncoder());
    //微信openid登陆
    auth.authenticationProvider(weChatAuthenticationProvider());
  }

2、分别定义两个拦截器,各自定义好需要拦截的登录url,并分别处理登录验证逻辑:

  • CustomAuthenticationFilter 拦截url:"/user/login"

  • WeChatAuthenticationFilter 拦截url:"/wechat/weChatLogin"

这两个url都在两个拦截器中有定义。

WeChatAuthenticationFilter wechatFilter = weChatAuthenticationFilter();
  CustomAuthenticationFilter customFilter = customAuthenticationFilter();
  ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
  validateCodeFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
  // http.httpBasic()    //httpBasic登录 BasicAuthenticationFilter
  // 必须在注册之后的过滤器之间才能安插过滤器
  http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
          .addFilterBefore(wechatFilter, UsernamePasswordAuthenticationFilter.class)
          .addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class)
      .addFilterAfter(validateCodeFilter, HttpMethodFilter.class)
      //表单登录,loginPage为登录请求的url,loginProcessingUrl为表单登录处理的URL
      .formLogin()
      .loginPage(loginPage)
      // 登录需要经过的url请求
      .loginProcessingUrl("/user/login")
      .loginProcessingUrl("/wechat/weChatLogin")

3、两个拦截器分别实现了自己的登陆成功和失败的处理逻辑

/**
 * 添加微信openid登陆验证的过滤器
 */
@Bean
public WeChatAuthenticationFilter weChatAuthenticationFilter() throws Exception {
  WeChatAuthenticationFilter filter = new WeChatAuthenticationFilter();
  filter.setAuthenticationManager(authenticationManagerBean());
  filter.setAuthenticationSuccessHandler(wxAuthenticationnSuccessHandler);
  filter.setAuthenticationFailureHandler(wxAuthenticationFailureHandler);
  return filter;
}

/**
 * 添加用户名和密码登陆验证的过滤器
 */
@Bean
public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
  CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
  filter.setAuthenticationManager(authenticationManagerBean());
  filter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
  filter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
  return filter;
}

五、自定义token

1、用户名和密码验证的token,需要账号密码作为验证

/**
 * @author: xxm
 * @description:用户名和密码验证的token
 */
public class CustomAuthenticationToken extends UsernamePasswordAuthenticationToken {
    /**
     *
     */
    private static final long serialVersionUID = -1076492615339314113L;

    public CustomAuthenticationToken(Object principal, Object credentials) {
        super(principal, credentials);
    }
    public CustomAuthenticationToken(Object principal, Object credentials,
                                     Collection<? extends GrantedAuthority> authorities) {
        super(principal, credentials, authorities);
    }
}

2、微信验证的token,只需要一个openid作为验证即可

/**
 * @author: xxm
 * @description:微信验证的token`
 */
public class WeChatAuthenticationToken extends UsernamePasswordAuthenticationToken {

    private static final long serialVersionUID = -6231962326068951783L;


    public WeChatAuthenticationToken(Object principal) {
        super(principal, "");
    }


    public WeChatAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
        super(principal, "", authorities);
    }

}

六、自定义拦截器

1、用户名和密码登陆验证的过滤器,重写了拦截的请求URL,并定义好用户名、密码的参数名称,从请求中获取到用户名、密码,生成CustomAuthenticationToken

拦截器中生成的CustomAuthenticationToken,账号和密码是从前台传过来,它将会和UserDetailsService中返回的CustomAuthenticationToken的账号密码进行对比验证,账号密码是否正确。(UserDetailsService中返回的CustomAuthenticationToken的账号密码是从数据库查出来的)

/**
 * @author: xxm
 * @description:用户名和密码登陆验证的过滤器
 */
public class CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";

    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
    private boolean postOnly = true;
    public CustomAuthenticationFilter() {
        //父类中定义了拦截的请求URL,/login的post请求,直接使用这个配置,也可以自己重写
        super("/user/login");
    }
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }

        String username = obtainUsername(request);
        String password = obtainPassword(request);

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        username = username.trim();

        CustomAuthenticationToken authRequest = new CustomAuthenticationToken(username, password);

        // Allow subclasses to set the "details" property
        setDetails(request,authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }

    protected String obtainPassword(HttpServletRequest request) {
        String password =request.getParameter(passwordParameter);
        return password == null ? "" : password;
    }

    /**
     * Enables subclasses to override the composition of the username, such as by
     * including additional values and a separator.
     *
     * @param request so that request attributes can be retrieved
     *
     * @return the username that will be presented in the <code>Authentication</code>
     * request token to the <code>AuthenticationManager</code>
     */
    protected String obtainUsername(HttpServletRequest request) {
        String username =request.getParameter(usernameParameter);
        return username == null ? "" : username;
    }
    protected void setDetails(HttpServletRequest request,
                              UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }
}

2、微信openid登陆验证的过滤器,重写了拦截的请求URL,并定义好openid的参数名称,从请求中获取到openid,生成WeChatAuthenticationToken

拦截器中生成的WeChatAuthenticationToken,openid是从前台传过来,它将会传递给WeChatAuthenticationProvider,并在该类中验证微信授权openid是否有效(根据openid查询数据库中是否关联用户即可)。

/**
 * @author: xxm
 * @description:微信openid登陆验证的过滤器
 */
public class WeChatAuthenticationFilter extends AbstractAuthenticationProcessingFilter {


    private String openidParameter = "openid";



    public WeChatAuthenticationFilter() {
        super("/wechat/weChatLogin");
        //super.setAuthenticationFailureHandler(new MyAuthenticationFailureHandler());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {

        if (!request.getMethod().equals(HttpMethod.GET.name())) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }

        String openid = obtainOpenid(request);
        if (openid == null || openid.length() == 0) {
            throw new BadCredentialsException("uid or openid is null.");
        }

        WeChatAuthenticationToken authRequest = new WeChatAuthenticationToken(openid);

        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));

        return this.getAuthenticationManager().authenticate(authRequest);

    }

    protected String obtainOpenid(HttpServletRequest request) {
        String openid = request.getParameter(this.openidParameter);
        return openid == null ? "" : openid.trim();
    }

}

七、自定义UserDetailsService

用户、密码登录采用了该种方式,从数据库查询出用户信息,并且查询出权限,返回带有权限的用户信息

/**
 * @author xxm
 * 定义UserDetailsService 接口
 */
@Service
public class CustomUserServiceImpl implements UserDetailsService {
  @Autowired
  UserControllerClient userControllerClient;

  // 授权过程
  @Override
  /** 根据数据库获得用户信息,并且查询出权限,返回带有权限的用户信息。 */
  public UserDetails loadUserByUsername(String username) {

    SysUser user = userControllerClient.getUserInfoByLoginName(username);
    if (user != null) {
      HttpServletRequest request =
          ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
      HttpSession session = request.getSession();
      session.setAttribute("username", username);
      List<String> permissionCodess = userControllerClient.findPermissionByAdminUserName(username);
      List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
      for (String permissionCode : permissionCodess) {
        if (permissionCode != null && permissionCode != "") {
          GrantedAuthority grantedAuthority =
                  new SimpleGrantedAuthority(permissionCode);
          grantedAuthorities.add(grantedAuthority);
        }
      }
      // 返回带有权限的user
      return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
    } else {
      throw new UsernameNotFoundException("admin: " + username + " do not exist!");
    }
  }
}

八、自定义Provider

微信登录采用了该种方式,根据WeChatAuthenticationFilter 传过来的token信息获取到openid,并根据openid查询微信关联账户,完成验证。

/**
 * @author: xxm
 * @description:
 * @date: 2021/3/11 16:07
 */
public class WeChatAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    UserWeChatClient userWeChatClient;
    @Autowired
    UserControllerClient userControllerClient;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        if (authentication.isAuthenticated()) {
            return authentication;
        }
        //获取过滤器封装的token信息
        WeChatAuthenticationToken authenticationToken = (WeChatAuthenticationToken) authentication;
        String openid = (String)authenticationToken.getPrincipal();
        SysUser user = null;
        UserWeChatDto uwcDto = new UserWeChatDto();
        uwcDto.setOpenId(openid);
        List<UserWeChatDto> uwcList = userWeChatClient.getListByParam(uwcDto);
        if (null != uwcList && uwcList.size()==1) {
            UserWeChatDto userWeChatDto = uwcList.get(0);
            //微信账号已经与网站账号关联
            //根据用户id查询用户
            user = userControllerClient.getUserById(userWeChatDto.getUserId());
            //存放session
            HttpServletRequest request =
                    ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            HttpSession session = request.getSession();
            session.setAttribute("username", user.getUsername());
        } else {
            //微信账号没有关联网站账号
            throw new BadCredentialsException("微信授权openid无效,请重新登陆");
        }
//        不通过
        if (user == null) {
            throw new BadCredentialsException("微信授权openid无效,请重新登陆");
        }
        // 根用户拥有全部的权限
        List<String> permissionCodess = userControllerClient.findPermissionByAdminUserName(user.getUsername());
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String permissionCode : permissionCodess) {
            if (permissionCode != null && permissionCode != "") {
                GrantedAuthority grantedAuthority =
                        new SimpleGrantedAuthority(permissionCode);
                authorities.add(grantedAuthority);
            }
        }
        WeChatAuthenticationToken authenticationResult = new WeChatAuthenticationToken(openid, authorities);

        return authenticationResult;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return WeChatAuthenticationToken.class.isAssignableFrom(authentication);
    }

}

九、自定义Handler

根据验证成功与失败返回相应数据和操作

/**
 *
 * @author: xxm
 * 功能描述: 微信登陆成功后操作
 * @param: 
 * @return: 
 */
@Service
public class WxAuthenticationnSuccessHandler implements AuthenticationSuccessHandler {

  @Override
  public void onAuthenticationSuccess(
          HttpServletRequest request, HttpServletResponse response, Authentication authentication)
      throws IOException, ServletException {
    response.sendRedirect("/index.html");
  }
}
/**
*
* @author: xxm
* 功能描述: 微信登录验证失败操作
* @param: 
* @return: 
*/
@Service
public class WxAuthenticationFailureHandler implements AuthenticationFailureHandler {
private ObjectMapper objectMapper = new ObjectMapper();

@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
 // 返回json数据
 Map result = new HashMap();
 result.put("wx_success", false);
 result.put("codeRtn", false);
 // 错误信息
 result.put("errorMsg", exception.getMessage());

 String json = objectMapper.writeValueAsString(result);
 response.setContentType("text/json;charset=utf-8");
 response.getWriter().write(json);
}
}

结束

从准备工作的资料,加上本文的相关代码,就可以实现账号用户名+微信网页授权登录集成在Spring Security。

作者:小苹果1357

来源:blog.csdn.net/xue317378914/article/

details/115250414

 
 
往期推荐:
Java 实现word、excel、ppt、txt等办公文件在线预览功能!

这个系统被吹爆了,Nginx 可视化配置监控一条龙快速搞定!

ChatGPT 详细介绍:原理、应用、如何试用,统统搞定!

如何搭建一台永久运行的个人服务器?

SpringBoot 实现动态配置及项目打包部署上线

SpringBoot 调用外部接口的三种方式

11.8K Star 数!超美观强的Nginx 可视化管理界面

Spring Boot 操作 Redis 的各种实现

28072bf6f2e13b19141e6dceedb1bc69.png

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringBoot + Spring Security多种登录方式:账号+微信网页授权登录 的相关文章

  • 将 Hibernate 对象序列化为 JSON 时抛出异常

    好吧 我正在使用 Hibernate 将一个小型数据库加载到一些表示表的类并与数据库交互 一切都很好 我真的可以看到所有结果 而且我没有任何空字段 所有这些都已被使用 这里我展示了 主 类 表 import javax persistenc
  • 无法在类对象的 ArrayList 中存储值。 (代码已编辑)

    这基本上是一个 Java 代码转换器 它涉及一个 GUI 让用户输入类类型 名称和方法 为了存储值 我创建了一个类VirtualClass与ArrayList
  • URL.setURLStreamHandlerFactory

    我正在使用带有嵌入式 Jetty 的可执行 jar 开发一个 Web 应用程序 我的jar包含一个依赖jar jar in jar 我参考了JarRsrcLoader and RsrcURLStreamHandlerFactory由 Ecl
  • 如何开始使用 Chainsaw for Log4j?

    我想开始使用 Chainsaw v2 几乎没有关于它的信息 我只找到了this http www velocityreviews com forums t140105 help using chainsaw for log4j html 但
  • 将过滤器添加到 Eclipse 中的 Project Explorer

    我想向 Project Explorer 添加一个新的过滤器 以向用户隐藏一些在 Eclipse RCP 应用程序中自动创建的项目 到目前为止我已经找到了两个扩展点 org eclipse ui ide resourceFilters 允许
  • java.lang.LinkageError:尝试重复的类定义

    为什么会发生错误以及如何修复它 02 13 02 pool 4 thread 2 WARN Exception in thread pool 4 thread 2 02 13 02 pool 4 thread 2 WARN java lan
  • 字符串池可以包含两个具有相同值的字符串吗? [复制]

    这个问题在这里已经有答案了 字符串池可以包含两个具有相同值的字符串吗 String str abc String str1 new String abc Will the second statement with new operator
  • Java Microsoft Excel API [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 从 @JsonProperty 值获取枚举常量

    我有一个标有 JsonProperty 的枚举 用于使用 Jackson 进行 JSON 序列化 反序列化 并且希望获取给定字符串 JsonProperty 的枚举值 public enum TimeBucket JsonProperty
  • Jenkins 的代码覆盖率 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何找到被点击的JLabel并从中显示ImageIcon?

    这是我的代码 我想知道哪个l单击 然后在新框架中显示该 ImageIcon e getSource 不起作用 final JFrame shirts new JFrame T shirts JPanel panel new JPanel n
  • 如何将 arraylist 从 servlet 传递到 javascript?

    我通过在属性中设置数组列表并将其转发到 jsp 来从 servlet 传递数组列表 Servlet ArrayList
  • JSch中如何设置文件类型和文件传输模式?

    我使用 Apache Common NetFTPClient并设置了我的ftpClient在上传文件之前使用如下所示的方法 ftpClient setFileType FTP BINARY FILE TYPE ftpClient setFi
  • 改变for循环的顺序?

    我遇到一种情况 我需要根据用户输入以不同的顺序循环遍历 xyz 坐标 所以我是 3D 空间中的一个区域 然后是一组像这样的 for 循环 for int x 0 x lt build getWidth x for int y 0 y lt
  • 如何在Java媒体框架中学习.wav持续时间?

    我正在尝试使用 java 媒体框架将 mov 文件与 wav 文件合并 因此我需要知道它们的持续时间 我怎样才能做到这一点 任何想法 将不胜感激 您可以使用以下方式了解声音文件的持续时间 即 VitalyVal 的第二种方式 import
  • JavaFX - 为什么多次将节点添加到窗格或不同的窗格会导致错误?

    我现在正在学习基本的 JavaFX 我不明白我正在阅读的书中的这一说法 不 诸如文本字段之类的节点只能添加到一个窗格中一次 将节点添加到多次窗格或不同的窗格将导致运行时错误 我可以从书中提供的UML图看出它是一个组合 但我不明白为什么 库类
  • Java中的回调接口是什么?

    SetObserver 接口的代码片段取自有效的Java 避免过度同步第67条 public interface SetObserver
  • 春季 CORS。在允许的来源中添加模式

    查看CORS的弹簧指南 以下代码启用所有允许的来源 public class MyWebMVCConfigurer extends WebMvcConfigurerAdapter Override public void addCorsMa
  • 摩尔斯电码 至 英语

    我现在的问题是让 摩尔斯电码转英语 正常工作 将英语转换为莫尔斯电码的第一部分工作正常 我知道以前已经有人问过这个问题 但我不知道我做错了什么 我知道我需要在某个地方进行拆分 但我只是不确定将其放在代码中的何处 现在 莫尔斯电码到英语的部分
  • Java、Spring、Hibernate找不到org.springframework.orm.hibernate3.LocalSessionFactoryBean

    我正在尝试制作 spring hibernate ant 项目 目前我收到此错误 HTTP Status 500 type Exception report message description The server encountere

随机推荐

  • 【qt应用软件Focus Note++】

    本专栏介绍了使用Qt开发的一些小型桌面软件 其中包括软件功能介绍 软件截图 主要代码等内容 此外 本专栏还提供完整的软件源码和安装包供有需要的同学下载 我的目标是开发一些简洁美观且实用的客户端小软件 如果能够为大家提供有用的软件或对学习有益
  • 赶紧来修炼内功~字符串函数详解大全(一)

    目录 1 strlen 重点 模拟实现 法一 法二 法三 2 strcpy 重点 模拟实现 3 strcat 重点 模拟实现 4 strcmp 重点 模拟实现 1 strlen strlen函数是用来计算字符串长度的 重点 字符串以 0 作
  • Ipsec phase1 and phase2

    终于明白是怎么回事了 困扰我好多天了 网上的资料鱼龙混杂 直到这位大虾的出现 第一阶段 有主模式和积极模式2种 只有remote vpn和Easy vpn是积极模式的 其他都是用主模式来协商的 让IKE对等体彼此验证对方并确定会话密钥 这个
  • 初探oVirt-体验

    日期 2015 9 2 2016 11 15 time 16 40 主机 node72 node73 node86 node93 目的 初探oVirt 体验 操作内容 一 基础环境 1 官方建议 1 oVirt Engine 需求 最低 双
  • 为OkGo网络请求增加自定义log功能

    OkGo是基于Okhttp3的封装 所以只需要增加自定义拦截器就可以实现自定义log OkGo有一个默认的log拦截器HttpLoggingInterceptor 如果没有特别需求则无需自定义 第一步自定义拦截器 参考OkGo中的拦截器实现
  • ctfshow-web4

    0x00 前言 CTF 加解密合集 CTF Web合集 0x01 题目 0x02 Write Up 和web3是相同的内容 这里可以通过任意文件读取的方式来进行利用 这里根据返回包知道是nginx 默认nginx日志是 var log ng
  • 如何批量上传Maven仓库jar包到Nexus3.x私服

    一 手动mvn命令上传单个Jar mvn deploy deploy file DgroupId com oracle DartifactId ojdbc6 Dversion 10 2 0 1 0 Dpackaging jar Dfile
  • 一、使用interrupt()中断线程

    当一个线程运行时 另一个线程可以调用对应的Thread对象的interrupt 方法来中断它 该方法只是在目标线程中设置一个标志 表示它已经被中断 并立即返回 这里需要注意的是 如果只是单纯的调用interrupt 方法 线程并没有实际被中
  • 执行pod setup 报错error: RPC failed; curl 18 transfer closed with outstanding read data remainin

    执行pod setup 报错 error RPC failed curl 18 transfer closed with outstanding read data remaining fatal the remote end hung u
  • 结构化稀疏----Learning with Structured Sparsity(学习与结构化稀疏)

    Structured Sparsity是在标准稀疏算法基础上 修改惩罚项而成 约束项为图像先验信息 迫使学习特征按照一定规则排列 行成有结构的字典 Standard sparsity Group Sparsity Group Sparsit
  • dfs全排列总结

    17 Letter Combinations of a Phone Number Medium 12161744Add to ListShare Given a string containing digits from 2 9 inclu
  • javascript代码混淆的原理

    如何对JavaScript进行保护 代码压缩 去除空格 换行等 代码加密 eval eval可以将其中的参数按照JavaScript的的语法进行解析并执行 其实就是将JavaScript的代码变成了eval的参数其中的一些字符会被按照特定的
  • Realtime_Multi-Person_Pose_Estimation demo.ipynb代码注释

    该部分可以帮助很好的理解论文的实现部分 源码地址 https github com ZheC Realtime Multi Person Pose Estimation 论文地址 https arxiv org abs 1611 08050
  • CVPR 2022

    点击下方卡片 关注 CVer 公众号 AI CV重磅干货 第一时间送达 作者 弃之 已授权转载 源 知乎 编辑 CVer https zhuanlan zhihu com p 478079763 PR一下我们在CVPR 2022上的pape
  • 12年经验资深产品经理领你从“产品小白”走向“产品大牛”

    当今社会 智能音箱 智能机器人 智能可穿戴设备等人工智能产品已经开始逐渐普及 而人工智能产品经理却少之又少 查看智联 拉勾 猎聘等多个招聘网站中企业招聘人工智能产品经理的岗位要求 发现不同公司在招聘人工智能产品经理时的标准都不一样 有些偏重
  • 单点登录的实现

    单点登录一般需要至少两个站 一个登录站 一个接入站 确切的说应该是N个接入站 各个站需要实现的功能如图 简单说明 登录站提供登录页面和退出功能 并提供用户信息的获取服务 接入站需要提供对应的登录成功回写服务 目的是为了存储本地登录信息 可以
  • VUE element-ui之el-popover弹出框在局部全屏下不显示问题及弹框、小箭头背景修改

    问题 局部全屏后el popover弹出框失效 解决方法
  • PackagesNotFoundError: The following packages are not available from current channels

    因为要用到lifelines 包 在cmd中使用conda install lifelines 显示如下错误 PackagesNotFoundError The following packages are not available fr
  • uniapp 离线打包webview无法上传图片问题

    离线打包上传文件选择文件上传失败 从文件点击选择的内容可以上传成功 其他路径进去上传失败 查了好久是因为清单文件的目标版本targetSdkVersion 写了29 改成28或者不填就好了
  • SpringBoot + Spring Security多种登录方式:账号+微信网页授权登录

    大家好 我是宝哥 一 概述 实现账号用户名 微信网页授权登录集成在Spring Security的思路 最重要的一点是要实现微信登录通过Spring Security安全框架时 不需要验证账号 密码 二 准备工作 要实现该功能 首先需要掌握