全局异常处理在common模块
为什么要使用全局异常处理:如果没有全局异常处理,比如不可预知的异常(空指针异常),返回浏览器的页面,人看上去就是一大堆乱码,非常的不好看。对人极其不友好,而且后端排查问题,看一大堆错误信息也不好排查,所以就用全局异常@GlobalExceptional进行封装。
1.只要是异常都会被管理
2.不能直接返回用户404 500 400,要返回两种异常 可预知异常(自己抛的异常)、不可预知(系统抛出的系统)。
3.所有异常统一响应消息处理回用户:程序员手动抛出(参数有问题,校验逻辑);服务异常,网络异常,系统维护,策略包,系统系统等。(写在common模块,所有微服务都要用到异常)
@ControllerAdvice 控制器增强类
@ExceptionHandler(Exception.class)不可控异常
@ExceptionHandler(CustomException.class)可预知异常
把类在spring容器中进行加载。
一旦其他服务引用了common这个包,就能够用这个全局异常类。
登陆user微服务 需要依赖 model common feign-api(对外接口) springbootstarterweb springbootstartertest springcloudalibabanacosdiscovery springcloudalibabanacosconfig
路径 zjj-leadnews-service\zjj-leadnews-user\src\main\java\com.zjj.user(config|controller.v1|controller.v2|mapper|service|UserApplication(启动类)|
创建zjj-leadnews-user模块 直接在zjj-leadnews-service右键new model;Parent是zjj-leadnews-service,Name是zjj-leadnews-user。
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.zjj.user.mapper")
@EnableFeignClients(basePackages = "com.zjj.apis")
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class,args);
}
}
如果是linux的话,改一下路径
debug有更多的错误信息
<?xml version="1.0" encoding="UTF-8"?>
>
<!--定义日志文件的存储地址,使用绝对路径-->
<!-- Console 输出设置 -->
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
utf8
<!-- 按照每天生成日志文件 -->
${LOG_HOME}/leadnews.%d{yyyy-MM-dd}.log
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
<!-- 异步输出 -->
0
512
>
-ref ref="CONSOLE"/>
>
>
>
<!---ref ref="ASYNC"/>-->
>
>
补充:
1.登陆的时候可以将用户信息 封装为UserInfo实体,然后ThreadLoacal.set(userInfo), 这样全局都可以使用。
2.游客登陆 没有输入用户名和密码,后端将用户名设置为0
package com.heima.user;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient@MapperScan("groupId(0).groupId(1).模块名.mapper")
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class,args);
}
}
server:
port: 51801
spring:
application: #nacos 这个端口的名字
name: leadnews-user # 该模块的名字 将该微服务给到注册中心
cloud:
nacos:
discovery: #注册
server-addr: 192.168.200.130:8848
config: #配置中心
server-addr: 192.168.200.130:8848
file-extension: yml #剩下的配置在nacos中配置
1.在nacos中创建配置文件:leadnews-user.yml
2.
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
### 注意
url: jdbc:mysql://192.168.200.130:3306/leadnews_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root ###不确定
password: root ###不确定
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml # :后不要空格
# 设置别名包扫描路径,通过该属性可以给包中的类注册别名 mapper.xml中写的别名
type-aliases-package: com.zjj.model.user.pojos
responseResult.setData();
data 属性 封装了 一个Object对象和一个token字符串
/**
* 通用的结果返回类
* @param
*/
public class ResponseResult<T> implements Serializable {
private String host;
private Integer code;
private String errorMessage;
private T data;
public ResponseResult() {
this.code = 200;
}
public ResponseResult(Integer code, T data) {
this.code = code;
this.data = data;
}
public ResponseResult(Integer code, String msg, T data) {
this.code = code;
this.errorMessage = msg;
this.data = data;
}
public ResponseResult(Integer code, String msg) {
this.code = code;
this.errorMessage = msg;
}
public static ResponseResult errorResult(int code, String msg) {
ResponseResult result = new ResponseResult();
return result.error(code, msg);
}
public static ResponseResult okResult(int code, String msg) {
ResponseResult result = new ResponseResult();
return result.ok(code, null, msg);
}
public static ResponseResult okResult(Object data) {
ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getErrorMessage());
if(data!=null) {
result.setData(data);
}
return result;
}
public static ResponseResult errorResult(AppHttpCodeEnum enums){
return setAppHttpCodeEnum(enums,enums.getErrorMessage());
}
public static ResponseResult errorResult(AppHttpCodeEnum enums, String errorMessage){
return setAppHttpCodeEnum(enums,errorMessage);
}
public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums){
return okResult(enums.getCode(),enums.getErrorMessage());
}
private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String errorMessage){
return okResult(enums.getCode(),errorMessage);
}
public ResponseResult<?> error(Integer code, String msg) {
this.code = code;
this.errorMessage = msg;
return this;
}
public ResponseResult<?> ok(Integer code, T data) {
this.code = code;
this.data = data;
return this;
}
public ResponseResult<?> ok(Integer code, T data, String msg) {
this.code = code;
this.data = data;
this.errorMessage = msg;
return this;
}
public ResponseResult<?> ok(T data) {
this.data = data;
return this;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public static void main(String[] args) {
//前置
/*AppHttpCodeEnum success = AppHttpCodeEnum.SUCCESS;
System.out.println(success.getCode());
System.out.println(success.getErrorMessage());*/
//查询一个对象
/*Map map = new HashMap();
map.put("name","zhangsan");
map.put("age",18);
ResponseResult result = ResponseResult.okResult(map);
System.out.println(JSON.toJSONString(result));*/
//新增,修改,删除 在项目中统一返回成功即可
/* ResponseResult result = ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
System.out.println(JSON.toJSONString(result));*/
//根据不用的业务返回不同的提示信息 比如:当前操作需要登录、参数错误
/*ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
System.out.println(JSON.toJSONString(result));*/
//查询分页信息
/*PageResponseResult responseResult = new PageResponseResult(1,5,50);
List list = new ArrayList();
list.add("itcast");
list.add("itheima");
responseResult.setData(list);
System.out.println(JSON.toJSONString(responseResult));*/
}
}
public static ResponseResult okResult(Object data) {
ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getErrorMessage());
if(data!=null) {
result.setData(data);
}
return result;
}
private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String errorMessage){
return okResult(enums.getCode(),errorMessage);
}
public enum AppHttpCodeEnum {
// 成功段0
SUCCESS(200,"操作成功"),
// 登录段1~50
NEED_LOGIN(1,"需要登录后操作"),
LOGIN_PASSWORD_ERROR(2,"密码错误"),
// TOKEN50~100
TOKEN_INVALID(50,"无效的TOKEN"),
TOKEN_EXPIRE(51,"TOKEN已过期"),
TOKEN_REQUIRE(52,"TOKEN是必须的"),
// SIGN验签 100~120
SIGN_INVALID(100,"无效的SIGN"),
SIG_TIMEOUT(101,"SIGN已过期"),
// 参数错误 500~1000
PARAM_REQUIRE(500,"缺少参数"),
PARAM_INVALID(501,"无效参数"),
PARAM_IMAGE_FORMAT_ERROR(502,"图片格式有误"),
SERVER_ERROR(503,"服务器内部错误"),
// 数据错误 1000~2000
DATA_EXIST(1000,"数据已经存在"),
AP_USER_DATA_NOT_EXIST(1001,"ApUser数据不存在"),
DATA_NOT_EXIST(1002,"数据不存在"),
// 数据错误 3000~3500
NO_OPERATOR_AUTH(3000,"无权限操作"),
NEED_ADMIND(3001,"需要管理员权限"),
// 自媒体文章 3501~3600
MATERIAL_REFERENCE_FAIL(3501,"素材引用失效");
int code;
String errorMessage;
AppHttpCodeEnum(int code, String errorMessage){
this.code = code;
this.errorMessage = errorMessage;
}
public int getCode() {
return code;
}
public String getErrorMessage() {
return errorMessage;
}
}
游客不生成token,全局过滤器无法解析会报错
http://localhost:xxxx/api/v1/login/lgoin_auth
这里是user微服务返回的token
ApUser dbUser = getOne(Wrappers.lambdaQuery().eq(ApUser::getPhone, dto.getPhone()));
mapper
@Mapper
public interface ApUserMapper extends BaseMapper<ApUser> {
}
service
public interface ApUserService extends IService<ApUser> {
/**
* app端登录
* @param dto
* @return
*/
public ResponseResult login(LoginDto dto);
}
serviceImpl
@Service
@Transactional
@Slf4j
public class ApUserServiceImpl extends ServiceImpl<ApUserMapper, ApUser> implements ApUserService {
/**
* app端登录
* @param dto
* @return
*/
@Override
public ResponseResult login(LoginDto dto) {
//1.正常登录
if(StringUtils.isNotBlank(dto.getPhone()) && StringUtils.isNotBlank(dto.getPassword())){
//1.1 查询用户
ApUser dbUser = getOne(Wrappers.<ApUser>lambdaQuery().eq(ApUser::getPhone, dto.getPhone()));
if(dbUser == null){
return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
}
//1.2 比对密码
String salt = dbUser.getSalt();
String dbUserPassword = dbUser.getPassword();
String pswd = DigestUtils.md5DigestAsHex((dto.getPassword() + salt).getBytes());
if(!dbUserPassword.equals(pswd)){
return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
}
//1.3 结果返回 user token
Map<String,Object> map = new HashMap<>();
dbUser.setSalt("");
dbUser.setPassword("");
map.put("user",dbUser);
map.put("token", AppJwtUtil.getToken(dbUser.getId().longValue())); //唯一主键来创建token
return ResponseResult.okResult(map);
}else {
//2.游客登录
Map<String,Object> map = new HashMap<>();
map.put("token", AppJwtUtil.getToken(0l));
return ResponseResult.okResult(map);
}
}
}
note:
两张表(just me)
ap_user App用户信息表
ap_user_realName App实名认证信息表
一般 记录 id自增主键、username、password(前端和后端各用一次盐)、salt、address、sex、nationality、phone、image、is_or_not_identity_certification是否实名认证、status正常为0异常为1、flag普通用户为0VIP用户为1、注册时间created_time注册后自动登陆等
1、model和common模块导入依赖
2、common导入swagger配置类
>
>io.springfox >
>springfox-swagger2 >
>
>
>io.springfox >
>springfox-swagger-ui >
>
package com.zjj.common.swagger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {
@Bean
public Docket buildDocket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(buildApiInfo())
.select()
// 要扫描的API(Controller)基础包
.apis(RequestHandlerSelectors.basePackage("com.zjj"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo buildApiInfo() {
Contact contact = new Contact("程序员","","");
return new ApiInfoBuilder()
.title("头条-平台管理API文档")
.description("头条后台api")
.contact(contact)
.version("1.0.0").build();
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.zjj.common.exception.ExceptionCatch,\
com.zjj.common.swagger.SwaggerConfiguration,\
com.zjj.common.swagger.Swagger2Configuration,\
com.zjj.common.aliyun.GreenImageScan,\
com.zjj.common.aliyun.GreenTextScan,\
com.zjj.common.tess4j.Tess4jClient,\
com.zjj.common.redis.CacheService
@RestController
@RequestMapping("/api/v1/login")
@Api(value = "app端用户登录",tags = "ap_user")
public class ApUserLoginController {
@Autowired
private ApUserService apUserService;
@ApiOperation("app端用户登录")
@PostMapping("/login_auth")
public ResponseResult login(@RequestBody LoginDto dto){
// if(true){
// throw new CustomException(AppHttpCodeEnum.NEED_ADMIND);
// }
// int a = 1/0;
return apUserService.login(dto);
}
}
@Data
public class LoginDto {
/**
* 手机号
*/
@ApiModelProperty(value = "手机号",required = true)
private String phone;
/**
* 密码
*/
@ApiModelProperty(value = "密码",required = true)
private String password;
}
集成了swagger 针对swagger进行封装
可以下载离线文档,可以让别人补全这个文档
在common模块即可
>
>com.github.xiaoymin >
>knife4j-spring-boot-starter >
>
package com.zjj.common.swagger;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
@EnableKnife4j
@Import(BeanValidatorPluginsConfiguration.class)
public class Swagger2Configuration {
@Bean(value = "defaultApi2")
public Docket defaultApi2() {
Docket docket=new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
//分组名称
.groupName("1.0")
.select()
//这里指定Controller扫描包路径
.apis(RequestHandlerSelectors.basePackage("com.zjj"))
.paths(PathSelectors.any())
.build();
return docket;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("头条API文档")
.description("头条API文档")
.version("1.0")
.build();
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.zjj.common.swagger.Swagger2Configuration
与service类似 多少了服务就应该有多少个网关
zjj-leadnews-service\zjj-leadnews-user
zjj-leadnews-gateway\zjj-leadnews-user-gateway
user写成了app 因为我们做的是app 登陆就是用这个app登陆的
>
>8 >
>8 >
>
>
>
>org.springframework.cloud >
>spring-cloud-starter-gateway >
>
>
>com.alibaba.cloud >
>spring-cloud-starter-alibaba-nacos-discovery >
>
>
>com.alibaba.cloud >
>spring-cloud-starter-alibaba-nacos-config >
>
>
>io.jsonwebtoken >
>jjwt >
>
>
package com.zjj.app.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class AppGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(AppGatewayApplication.class,args);
}
}
server:
port: 51601
spring:
application:
name: leadnews-app-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.200.130:8848
config:
server-addr: 192.168.200.130:8848
file-extension: yml
nacos网关跨域设置
spring:
cloud:
gateway:
globalcors:
add-to-simple-url-handler-mapping: true #跨域 后期用的
corsConfigurations:
'[/**]':
allowedHeaders: "*" ##允许所以域 都可以访问该问服务
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- DELETE
- PUT
- OPTION
routes:
# 平台管理
- id: user #随便写
uri: lb://leadnews-user #均衡负载 给到某个微服务器去访问 可以创建多个登录的微服务
predicates: #断言 请求该微服务必须要/user localhost:51601/user/api/v1/login/...
# 去掉断言前缀 实际访问的是 leadnews-user对应的ip和port/api/v1
- Path=/user/**
filters:
- StripPrefix= 1 #伪装 访问的时候可以去掉user
StripPrefix=1 去掉前缀一个 /user/** 去掉前缀user user的作用表示连接的是user微服务 实际地址会去掉user 可以认为user是伪装
访问ip
http://localhost:8092(网关ip)/user/api/v1/login/login_in => leadnews-user微服务的api/v1/login/login_in接口
predicates 谓词,判断
注册的代码在哪里?没讲,大概的思路就是输入账号密码,然后MD5(密码+salt(DDUtil.random())),把salt和MD5加密后的密码存到数据库。每个用户都有唯一的一个salt保存在数据库。
用户登陆的时候返回token 用户每次请求(除了登陆请求)会携带token,会被网关全局过滤器拦截
404 找不到任何东西
传token的方式 因为是在headers拿 所有封装在headers给
监听器 > 过滤器(AOP日志记录) > 拦截器(AOP日志记录) > servlet执行 > 拦截器 > 过滤器 > 监听器
可以说URL是URI(URL是URI的子集)
过滤器
@Component //放入到spring容器才能使得该类生效
@Slf4j
public class AuthorizeFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//判断是否为登录 可以说URL是URI(URL是URI的子集)URL和URI之间的区别是什么
if(request.getURI().getPath().contains("login")){
//放行
return chain.filter(exchange);
}
//校验token是否存在 Request Headers 里面 包含 为key的token 还包含 Accept-Encoding、Cache-Control Cookie Host Origin User-Agent、Connection、Content-Type等
String token = request.getHeaders().getFirst("token");
if(StringUtils.isEmpty(token)){
//返回401
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//结束请求
return response.setComplete();
}
//token是否有效 之前是用AppJwtUtil.getToken(主键id)得到的token
try {
Claims claims = AppJwtUtil.getClaimsBody(token);
//解析完得到的claims然后判断是否过期 -1:有效,0:有效,1:过期,2:过期
int result = AppJwtUtil.verifyToken(claims);
if(result == 1 || result == 2){
//返回401
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//获取用户id claims里面包含 {"id","userId"} 放到headers中 这样拦截器或者其他就能拿到headers中的id
Object userId = claims.get("id");
ServerHttpRequest serverHttpRequest = request.mutate().headers(new Consumer<HttpHeaders>() {
@Override
public void accept(HttpHeaders httpHeaders) {
httpHeaders.add("userId", userId + "");
}
}).build();
//重置请求
exchange.mutate().request(serverHttpRequest);
}catch (Exception e){
//打印堆栈错误信息
e.printStackTrace();
//返回401 解析失败
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//放行
return chain.filter(exchange);
}
/**
* 优先级设置 值越小,优先级越高
* 过滤器的数量 过滤器0->过滤器1—>...controller
* 监听器 > 过滤器(AOP日志记录) > 拦截器(AOP日志记录) > servlet执行 > 拦截器 > 过滤器 > 监听器
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
AppJwtUtil
public class AppJwtUtil {
// TOKEN的有效期一天(S) 1*24*60*60
private static final int TOKEN_TIME_OUT = 3_600;
// 加密KEY
private static final String TOKEN_ENCRY_KEY = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY";
// 最小刷新间隔(S)
private static final int REFRESH_TIME = 300;
// 生产ID
public static String getToken(Long id){
Map<String, Object> claimMaps = new HashMap<>();
claimMaps.put("id",id); // claims.get("id") 拿到id 你输入的就是id claims实际上是个map
// claimMaps.put("userName", name); map里面随便你存什么信息
long currentTime = System.currentTimeMillis(); // 毫秒
return Jwts.builder()
.setId(UUID.randomUUID().toString())
.setIssuedAt(new Date(currentTime)) //签发时间
.setSubject("system") //说明
.setIssuer("zjj") //签发者信息
.setAudience("app") //接收用户
.compressWith(CompressionCodecs.GZIP) //数据压缩方式
.signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式
.setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) //过期时间戳
.addClaims(claimMaps) //cla信息
.compact();
}
/**
* 获取token中的claims信息
*
* @param token
* @return
*/
private static Jws<Claims> getJws(String token) {
return Jwts.parser()
.setSigningKey(generalKey())
.parseClaimsJws(token);
}
/**
* 获取payload body信息
* 该方法用于解析token
* 根据token拿到Claims对象
* @param token
* @return
*/
public static Claims getClaimsBody(String token) {
try {
return getJws(token).getBody();
}catch (ExpiredJwtException e){
return null;
}
}
/**
* 获取hearder body信息
*
* @param token
* @return
*/
public static JwsHeader getHeaderBody(String token) {
return getJws(token).getHeader();
}
/**
* 是否过期
*
* @param claims
* @return -1:有效,0:有效,1:过期,2:过期
*/
public static int verifyToken(Claims claims) {
if(claims==null){
return 1;
}
try {
claims.getExpiration()
.before(new Date());
// 需要自动刷新TOKEN
if((claims.getExpiration().getTime()-System.currentTimeMillis())>REFRESH_TIME*1000){
return -1;
}else {
return 0;
}
} catch (ExpiredJwtException ex) {
return 1;
}catch (Exception e){
return 2;
}
}
/**
* 由字符串生成加密key
*
* @return
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
public static void main(String[] args) {
/* Map map = new HashMap();
map.put("id","1102");*/
System.out.println(AppJwtUtil.getToken(1102L));
Jws<Claims> jws = AppJwtUtil.getJws("eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAADWLQQqEMAwA_5KzhURNt_qb1KZYQSi0wi6Lf9942NsMw3zh6AVW2DYmDGl2WabkZgreCaM6VXzhFBfJMcMARTqsxIG9Z888QLui3e3Tup5Pb81013KKmVzJTGo11nf9n8v4nMUaEY73DzTabjmDAAAA.4SuqQ42IGqCgBai6qd4RaVpVxTlZIWC826QA9kLvt9d-yVUw82gU47HDaSfOzgAcloZedYNNpUcd18Ne8vvjQA");
Claims claims = jws.getBody();
System.out.println(claims.get("id")); // claims.get("id") 拿到id 你输入的就是id claims实际上是个map
}
}
index.html里面的地址有app就会访问下面的地址然后到upstream
反向代理找到网关 反向代理中间件帮后端找到网关服务器
nginx -s reload
D:\file\001developer\nginx-1.18.0\conf\leadnews.conf
一个nginx可以访问多了conf文件 可以有多个静态页面
多了一个heima-leadnews-wemedia.conf
重启的话 nginx -s reload
对称加密 密钥都一样 加密解密 TOKEN_ENCRY_KEY都一样
非对称加密 KEY不一样
不可逆加密 token解析不出来token
A.B.C
package com.heima.utils.thread;
import com.heima.model.wemedia.pojos.WmUser;
public class WmThreadLocalUtil {
private static final ThreadLocal<WmUser> WM_USER_THREAD_LOCAL = new ThreadLocal<>();
//存入线程 静态方法方便调用
public static void setUser(WmUser wmUser){
WM_USER_THREAD_LOCAL.set(wmUser);
}
//从线程中获取
public static WmUser getUser(){
return WM_USER_THREAD_LOCAL.get();
}
//清理
public static void clear(){
WM_USER_THREAD_LOCAL.remove();
}
}
package com.heima.wemedia.config;
import com.heima.wemedia.interceptor.WmTokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//使得拦截器生效
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new WmTokenInterceptor()).addPathPatterns("/**");
}
}
package com.heima.wemedia.interceptor;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.thread.WmThreadLocalUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// public class WebMvcConfig implements WebMvcConfigurer 使得拦截器生效
public class WmTokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取用户数据
String userId = request.getHeader("userId");
if (StringUtils.isNotBlank(userId)) {
WmUser wmUser = new WmUser();
wmUser.setId(Integer.valueOf(userId));
WmThreadLocalUtil.setUser(wmUser);
}
return true;
}
/*@Override //抛异常不会走这里 清理不了数据 所以不用这个方法
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}*/
/**
* 抛异常会走这 且用来清理数据 防止内存溢出
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
WmThreadLocalUtil.clear();
}
}
maven - 3.3.9 (现在学习用的)/ 3.6.1
jdk 1.8
Intellij Idea 2020.1.2
Git