临时接了一个小项目,有需要搭一个小项目,简单记录一下项目搭建过程以及整合登录功能。

1.首先拿到的是一个码云地址,里面是一个空的文件夹,只有一个

2. 拿到HTTPS码云项目地址链接,在IDEA中clone输入项目地址

3. 在项目根目录文件夹下右击,选择Add Frameworks Support, 添加maven框架支持

4. 然后得到pom.xml文件, 在pom文件中配置springboot

点击查看代码
    <groupId>groupId</groupId>
<artifactId>resource-manager-background</artifactId>
<version>1.0-SNAPSHOT</version> <parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.12.RELEASE</version>
</parent>

5. 引入其他相关依赖 mysql druid mybatisplus

点击查看代码
<dependencies>
<!-- mysqlconnect -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.1</version>
</dependency>
<!-- Mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.0</version>
</dependency>
<!-- jwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<!-- security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- springweb -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies> <properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<swagger2.version>2.9.2</swagger2.version>
</properties>

6. 创建security文件夹,创建Security核心配置类

点击查看代码
package com.gameresource.security.config;

import com.gameresource.base.bean.Resp;
import com.gameresource.base.constant.RespConstant;
import com.gameresource.security.filter.JwtAuthTokenFilter;
import com.gameresource.security.util.RespUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
private IgnoreUrlsConfig ignoreUrlsConfig; @Override
protected void configure(HttpSecurity http) throws Exception {
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
for (String url : ignoreUrlsConfig.getUrls()) {
registry.antMatchers(url).permitAll();
}
registry.antMatchers(HttpMethod.OPTIONS).permitAll();
registry.and()
.authorizeRequests()
.anyRequest()
.authenticated() //关闭跨站请求防护及不使用防护
.and()
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) //自定义权限拒绝处理类
.and()
.exceptionHandling()
.accessDeniedHandler(((request, response, accessDeniedException) -> {
RespUtils.outJson(response, Resp.code(RespConstant.NOT_PERMISSION));
}))
.authenticationEntryPoint((request, response, authenticationException) -> {
RespUtils.outJson(response, Resp.code(RespConstant.NOT_AUTHORIZE));
}) //自定义权限拦截器JWT过滤器
.and()
.addFilterBefore(jwtAuthTokenFilter(), UsernamePasswordAuthenticationFilter.class);
//关闭默认登录
http.formLogin().disable().httpBasic().disable();
} @Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/auth/login",
"/doc.html",
"/swagger-ui.html",
"/v2/api-docs",
"/swagger-resources/**",
"/**/*.js",
"/**/*.css",
"/**/*.png",
"/**/*.ico"
);
} @Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
} @Bean
public JwtAuthTokenFilter jwtAuthTokenFilter() {
return new JwtAuthTokenFilter();
} }

7. 自定义JWT过滤器

点击查看代码
package com.gameresource.security.filter;

import cn.hutool.core.util.StrUtil;
import com.gameresource.base.bean.Resp;
import com.gameresource.base.constant.RespConstant;
import com.gameresource.security.constant.SecurityConstant;
import com.gameresource.security.config.IgnoreUrlsConfig;
import com.gameresource.security.util.JwtTokenUtil;
import com.gameresource.security.util.RespUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; /**
* 自定义JWT过滤器
*/
@Slf4j
public class JwtAuthTokenFilter extends OncePerRequestFilter { @Autowired
private UserDetailsService userDetailsService; @Autowired
private IgnoreUrlsConfig ignoreUrlsConfig;
@Autowired
private JwtTokenUtil jwtTokenUtil; @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader(SecurityConstant.TOKEN_HEADER);
String uri = request.getRequestURI();
if (StrUtil.startWith(authHeader, SecurityConstant.TOKEN_HEAD)) {
try {
String token = StrUtil.trim(authHeader.substring(SecurityConstant.TOKEN_HEAD.length()));
String username = jwtTokenUtil.getUserNameFromToken(token); log.info("checking username:{}", username);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
if (jwtTokenUtil.validateToken(token)) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
log.info("pass user: {}", username);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
} catch (BadCredentialsException exception) {
RespUtils.outJson(response, Resp.fail(exception.getMessage()));
return;
} catch (Exception exception) {
log.error("Spring Security 校验异常: ", exception);
RespUtils.outJson(response, Resp.code(RespConstant.NOT_AUTHORIZE));
return;
}
}
chain.doFilter(request, response);
} }

8. 创建JWT工具类

点击查看代码
package com.gameresource.security.util;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.gameresource.security.constant.SecurityConstant;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component; import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit; /**
* jwt工具类
*/ @Slf4j
@Component
@RequiredArgsConstructor
public class JwtTokenUtil { @Value("${project.name}")
private String projectName;
@Value("${Jwt.secret}")
private String secret;
@Value("${Jwt.expiration}")
private Integer expiration; private final StringRedisTemplate redisTemplate; public boolean validateToken(String token) {
String username = getUserNameFromToken(token); String redisKey = TokenRedisKeyUtils.installAuthToken(projectName, username);
if (Boolean.FALSE.equals(redisTemplate.hasKey(redisKey))) {
throw new BadCredentialsException("Token不存在或已过期");
} String redisToken = redisTemplate.opsForValue().get(redisKey);
if (!StrUtil.equals(redisToken, token)) {
throw new BadCredentialsException("无效token");
}
return true;
} /**
* 从token中获取用户名
*/
public String getUserNameFromToken(String token) {
return Convert.toStr(getValueFromToken(token, SecurityConstant.CLAIM_KEY_USERNAME));
} private Object getValueFromToken(String token, String key) {
Claims claim = getClaimsFromToken(token);
return claim != null ? claim.get(key) : null;
} private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception exception) {
log.error("jwt格式验证失败: {}", token);
}
return claims;
} public String generateToken(UserDetails userDetails) {
return generateToken(userDetails, null);
} private String generateToken(UserDetails userDetails, Map<String, Object> claims) {
if (claims == null) {
claims = new HashMap<>();
}
claims.put(SecurityConstant.CLAIM_KEY_USERNAME, userDetails.getUsername()); int ttl = expiration * 1000;
DateTime dateTime = DateUtil.offsetMillisecond(new Date(), ttl);
String token = Jwts.builder()
.setClaims(claims)
.setExpiration(dateTime)
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
String redisKey = TokenRedisKeyUtils.installAuthToken(projectName, userDetails.getUsername());
redisTemplate.opsForValue().set(redisKey, token, ttl, TimeUnit.MILLISECONDS);
return token;
} public Boolean removeToken(String username) {
return redisTemplate.delete(TokenRedisKeyUtils.installAuthToken(projectName, username));
}
}

9. 响应工具类

点击查看代码
package com.gameresource.security.util;

import com.alibaba.fastjson.JSON;
import com.gameresource.base.bean.Resp;
import org.springframework.http.MediaType;
import org.springframework.security.core.parameters.P; import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter; public class RespUtils { public static void outJson(HttpServletResponse response, Resp resp) {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_OK);
response.setCharacterEncoding("utf-8"); try (PrintWriter printWriter = response.getWriter();) {
printWriter.print(JSON.toJSONString(resp));
printWriter.flush();
}catch (IOException exception) {
throw new RuntimeException();
} } }

10. UserDetailsService的自定义

点击查看代码
package com.gameresource.security.service;

import com.gameresource.manage.sys.SysUserManager;
import lombok.RequiredArgsConstructor;
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; @Service
@RequiredArgsConstructor
public class AuthUserDetailService implements UserDetailsService { private final SysUserManager userManager; @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userManager.findUserByName(username);
}
}

11. 各种类的实现代码

1. SysUserManager

点击查看代码

package com.gameresource.manage.sys; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.gameresource.module.sys.domain.AuthUser;
import com.gameresource.module.sys.entity.SysUserEntity;
import com.gameresource.module.sys.entity.SysUserRoleEntity;
import com.gameresource.module.sys.service.ISysUserRoleService;
import com.gameresource.module.sys.service.ISysUserService;
import com.gameresource.security.constant.SecurityConstant;
import com.gameresource.security.util.JwtTokenUtil;
import com.sun.org.apache.xpath.internal.operations.Bool;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
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.Component; import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors; @RequiredArgsConstructor
@Component
@Slf4j
public class SysUserManager { private final ISysUserService userService;
@Autowired
private PasswordEncoder passwordEncoder;
private final ISysUserRoleService userRoleService;
private final JwtTokenUtil jwtTokenUtil; public String login(String username, String password) {
UserDetails userDetails = findUserByName(username);
if (passwordEncoder.matches(password, userDetails.getPassword())) {
throw new BadCredentialsException("用户名或密码错误");
}
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
return jwtTokenUtil.generateToken(userDetails);
} public UserDetails findUserByName(String username) {
SysUserEntity userEntity = userService.getByUsername(username);
if (ObjectUtil.isNull(userEntity)) {
throw new UsernameNotFoundException("用户不存在!");
}
if (!userEntity.getStatus()) {
throw new RuntimeException("此账户已被禁用!");
} List<SysUserRoleEntity> roleEntityList = userRoleService.getByUserId(userEntity.getId());
List<Integer> roleList = roleEntityList.stream().map(SysUserRoleEntity::getRoleId).collect(Collectors.toList());
return AuthUser.create(userEntity, roleList, null);
} public boolean logout(HttpServletRequest request) {
String token = request.getHeader(SecurityConstant.TOKEN_HEADER);
if (StrUtil.isBlank(token)) {
return false;
}
token = token.substring(SecurityConstant.TOKEN_HEAD.length());
String username = jwtTokenUtil.getUserNameFromToken(token);
Boolean logout = jwtTokenUtil.removeToken(username);
if (!logout) {
log.error("============登出失败============");
}
log.info("=======================用户退出{}======================", username);
return true;
}
}

2. AuthUser

点击查看代码
package com.gameresource.module.sys.domain;

import cn.hutool.core.collection.CollUtil;
import com.gameresource.module.sys.entity.SysUserEntity;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors; @Data
@Accessors(chain = true)
public class AuthUser implements UserDetails { /**
* 用户id
*/
private int userId; /**
* 用户名
*/
private String username; /**
* 密码
*/
private String password; /**
* 是否启用
*/
private boolean status; /**
* 用户角色列表
*/
private List<Integer> roleIds; /**
* 用户权限列表
*/
private Collection<? extends GrantedAuthority> authorities; public static UserDetails create(SysUserEntity sysUser, List<Integer> roleIds, List<String> perms) {
if (sysUser == null) {
return null;
}
List<GrantedAuthority> authorities = Collections.emptyList();
if (CollUtil.isNotEmpty(perms)) {
authorities = perms.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
} return new AuthUser()
.setUserId(sysUser.getId())
.setStatus(sysUser.getStatus())
.setUsername(sysUser.getUsername())
.setPassword(sysUser.getPassword())
.setRoleIds(CollUtil.isNotEmpty(roleIds) ? roleIds : Collections.emptyList())
.setAuthorities(authorities);
} @Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
} @Override
public String getPassword() {
return password;
} @Override
public String getUsername() {
return username;
} @Override
public boolean isAccountNonExpired() {
return false;
} @Override
public boolean isAccountNonLocked() {
return false;
} @Override
public boolean isCredentialsNonExpired() {
return false;
} @Override
public boolean isEnabled() {
return status;
}
}

12. 登录接口

点击查看代码
package com.gameresource.web.sys;

import cn.hutool.core.lang.Assert;
import com.gameresource.base.bean.Resp;
import com.gameresource.manage.sys.SysUserManager;
import com.gameresource.module.sys.domain.AuthUser;
import com.gameresource.module.sys.domain.AuthUserVo;
import com.gameresource.module.sys.domain.LoginReqDto;
import com.gameresource.util.SecurityUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid; @Slf4j
@Api(tags = "登录")
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class LoginController { private final SysUserManager sysUserManager; @ApiOperation("用户登录")
@PostMapping("/login")
public Resp<AuthUserVo> login(@Valid @RequestBody LoginReqDto loginReq) {
try {
log.info("===============loginReq=====================username:[{}],password:[{}]",loginReq.getUsername(), loginReq.getPassword());
String token = sysUserManager.login(loginReq.getUsername(), loginReq.getPassword());
Assert.notBlank(token, "用户名或密码错误");
AuthUser authUser = SecurityUtil.getCurrentUser();
log.info("========================用户{}登录成功!=============================", authUser.getUsername());
return Resp.ok(AuthUserVo.create(authUser, token));
}catch (Exception e) {
log.info("登录失败: ", e);
return Resp.fail(e.getMessage());
}
} @ApiOperation("用户退出")
@PostMapping("/logout")
public Resp<Boolean> logout(HttpServletRequest request) {
return Resp.result(sysUserManager.logout(request));
} }

13. 至此结束

其实登录的核心部分就是WebSecurityConfigurerAdapter下的configure方法,我们通过自定义了一个SecurityConfig类来继承了WebSecurityConfigurerAdapter类,并重写配置类的各种选项达到自定义的效果,另外一个就是OncePerRequestFilter来过滤各种请求,在这里我们会处理请求并验证token,这只是项目中比较简单的一种写法,另外security还有很多其他的配置,在这里只是列举了其中的一种较为简单的方式,才疏学浅,还需要多加学习,共勉!

SpringBoot项目搭建 + Jwt登录的更多相关文章

  1. SpringBoot 项目搭建(详细介绍+案例源码)

    SpringBoot 项目搭建 SpringBoot 项目整合源码 SpringBoot 项目整合 一.项目准备 1.1 快速创建 SpringBoot 项目 1.2 标准项目结构图如下 1.3 添加 ...

  2. SpringBoot之入门教程-SpringBoot项目搭建

    SpringBoot大大的简化了Spring的配置,把Spring从配置炼狱中解救出来了,以前天天配置Spring和Mybatis,Springmvc,Hibernate等整合在一起,感觉用起来还是挺 ...

  3. SpringBoot项目搭建与打包

    一.环境准备 本地java环境jdk1.8 Maven版本3.5.2 IDE工具idea2017 二.SpringBoot微服务搭建 1.点击File >> New >> Pr ...

  4. springboot系列二、springboot项目搭建

    一.官网快速构建 1.maven构建项目 1.访问http://start.spring.io/ 2.选择构建工具Maven Project.Spring Boot版本2.1.1以及一些工程基本信息, ...

  5. 从零开始的SpringBoot项目搭建

    前言 今天是我加入博客园的第一天今天刚好学习到SpringBoot,就顺便记录一下吧 一.创建项目 1.创建工程 ① 通过File > New > Project,新建工程,选择Sprin ...

  6. Springboot项目搭建(3)-shiro登录

    shiro简述+实现简单登录:https://www.jianshu.com/p/7f724bec3dc3

  7. springboot项目搭建及常用技术整合

    一.在你建立的工程下创建 Module 选择Spring initializr创建. 二.在Type处选择: Maven Project(项目的构建工具) 三.创建依赖时勾上web,mybatis,m ...

  8. oa_mvc_easyui_项目搭建及登录页面验证码(1)

    1.空项目的搭建,三层的搭建(各层之中的引用) webapp:bll,model,common bll:dal,model dal:model 2.SQL表 ItcastDb:T_UserInfo,T ...

  9. springboot项目搭建:结构和入门程序

    Spring Boot 推荐目录结构 代码层的结构 根目录:com.springboot 1.工程启动类(ApplicationServer.java)置于com.springboot.build包下 ...

随机推荐

  1. Linux挂载iso镜像、配置本地yum源

    Linux挂载iso镜像.配置本地yum源 1.备份原yum源配置文件 [root@localhost ~]# ll /etc/yum.repos.d/ [root@localhost ~]# mkd ...

  2. C语言 - 基础数据结构和算法 - 企业链表

    听黑马程序员教程<基础数据结构和算法 (C版本)>,照着老师所讲抄的, 视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1 喜欢的朋友可 ...

  3. 一文澄清网上对 ConcurrentHashMap 的一个流传甚广的误解!

    大家好,我是坤哥 上周我在极客时间某个课程看到某个讲师在讨论 ConcurrentHashMap(以下简称 CHM)是强一致性还是弱一致性时,提到这么一段话 这个解释网上也是流传甚广,那么到底对不对呢 ...

  4. 22.LVS+Keepalived 高可用群集

    LVS+Keepalived 高可用群集 目录 LVS+Keepalived 高可用群集 keepalived工具介绍 Keepalived实现原理剖析 VRRP(虚拟路由冗余协议) VRRP 相关术 ...

  5. SAP创建XML 文件

    TYPES: BEGIN OF xml_line_type, data(256) TYPE x, END OF xml_line_type, xml_tab_type TYPE TABLE OF xm ...

  6. ServletContext 对象

    概念:代表整个Web应用 可以和程序的容器通信 (服务器) 获取 通过request对象获取  request.getServletContext(); 通过HTTPServlet获取  this.g ...

  7. 查看mysql安装目录、安装路径

    show variables like "%CHAR%" 我是在Navicat中输入这个命令后查的.

  8. Java ArrayList和LinkedList

    目录 集合的概念 集合体系结构 常用list集合 list集合的特点 ArrayList LinkedList 创建对象 常用方法 遍历 ArrayList和LinkedList的区别 集合的概念 ​ ...

  9. halcon变量窗口的图像变量不显示,重启软件和电脑都没用

    有幸遇到halcon变量窗口的图像变量不显示,重启软件和电脑都没用这个沙雕问题,也是找了蛮久才发现解决办法特意记录一下. 这是正常情况下的窗口(左边)和图像变量不显示的窗口(右边): 解决方法: 鼠标 ...

  10. charles(CA证书)的app端安装

    在使用charles进行的app抓包的时候势必需要对他进行配置: 1. pc端: 第一步: http请求接收charles > proxy > proxy setting > por ...