1 异常处理

全局异常处理在common模块
为什么要使用全局异常处理:如果没有全局异常处理,比如不可预知的异常(空指针异常),返回浏览器的页面,人看上去就是一大堆乱码,非常的不好看。对人极其不友好,而且后端排查问题,看一大堆错误信息也不好排查,所以就用全局异常@GlobalExceptional进行封装。


1.只要是异常都会被管理
2.不能直接返回用户404 500 400,要返回两种异常 可预知异常(自己抛的异常)、不可预知(系统抛出的系统)。
3.所有异常统一响应消息处理回用户:程序员手动抛出(参数有问题,校验逻辑);服务异常,网络异常,系统维护,策略包,系统系统等。(写在common模块,所有微服务都要用到异常)
@ControllerAdvice 控制器增强类
@ExceptionHandler(Exception.class)不可控异常
@ExceptionHandler(CustomException.class)可预知异常

使得异常类生效的加个配置

把类在spring容器中进行加载。
一旦其他服务引用了common这个包,就能够用这个全局异常类。

2 登陆模块

2.1思路分析

注册:知道怎么注册才知道怎么登录


直接123456进行MD5加密,容易被破译。

登录


登陆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"?><configuration><!--定义日志文件的存储地址,使用绝对路径--><property name="LOG_HOME" value="e:/logs"/><!-- Console 输出设置 --><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern><charset>utf8</charset></encoder></appender><!-- 按照每天生成日志文件 --><appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--日志文件输出的文件名--><fileNamePattern>${LOG_HOME}/leadnews.%d{yyyy-MM-dd}.log</fileNamePattern></rollingPolicy><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><!-- 异步输出 --><appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"><!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --><discardingThreshold>0</discardingThreshold><!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --><queueSize>512</queueSize><!-- 添加附加的appender,最多只能添加一个 --><appender-ref ref="FILE"/></appender><logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false"><appender-ref ref="CONSOLE"/></logger><logger name="org.springframework.boot" level="debug"/><root level="info"><!--<appender-ref ref="ASYNC"/>--><appender-ref ref="FILE"/><appender-ref ref="CONSOLE"/></root>
</configuration>

补充:
1.登陆的时候可以将用户信息 封装为UserInfo实体,然后ThreadLoacal.set(userInfo), 这样全局都可以使用。
2.游客登陆 没有输入用户名和密码,后端将用户名设置为0

2.2 user模块对应的引导类

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); }
}

2.3 bootstrap.yml

server:port: 51801
spring:application: #nacos 这个端口的名字name: leadnews-user # 该模块的名字 将该微服务给到注册中心cloud:nacos:discovery: #注册server-addr: 192.168.200.130:8848config: #配置中心server-addr: 192.168.200.130:8848file-extension: yml #剩下的配置在nacos中配置

2.4 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

responseResult.setData();
data 属性 封装了 一个Object对象和一个token字符串

/*** 通用的结果返回类* @param <T>*/
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);}

枚举 AppHttpCodeEnum

public enum AppHttpCodeEnum {// 成功段0SUCCESS(200,"操作成功"),// 登录段1~50NEED_LOGIN(1,"需要登录后操作"),LOGIN_PASSWORD_ERROR(2,"密码错误"),// TOKEN50~100TOKEN_INVALID(50,"无效的TOKEN"),TOKEN_EXPIRE(51,"TOKEN已过期"),TOKEN_REQUIRE(52,"TOKEN是必须的"),// SIGN验签 100~120SIGN_INVALID(100,"无效的SIGN"),SIG_TIMEOUT(101,"SIGN已过期"),// 参数错误 500~1000PARAM_REQUIRE(500,"缺少参数"),PARAM_INVALID(501,"无效参数"),PARAM_IMAGE_FORMAT_ERROR(502,"图片格式有误"),SERVER_ERROR(503,"服务器内部错误"),// 数据错误 1000~2000DATA_EXIST(1000,"数据已经存在"),AP_USER_DATA_NOT_EXIST(1001,"ApUser数据不存在"),DATA_NOT_EXIST(1002,"数据不存在"),// 数据错误 3000~3500NO_OPERATOR_AUTH(3000,"无权限操作"),NEED_ADMIND(3001,"需要管理员权限"),// 自媒体文章 3501~3600MATERIAL_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

MybatisPlus的使用

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*/@Overridepublic 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  tokenMap<String,Object> map = new HashMap<>();dbUser.setSalt("");dbUser.setPassword("");map.put("user",dbUser);map.put("token", AppJwtUtil.getToken(dbUser.getId().longValue())); //唯一主键来创建tokenreturn 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注册后自动登陆等

swagger降低后端人员编写接口文档负担

访问地址 服务加端口


1、model和common模块导入依赖
2、common导入swagger配置类

<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId></dependency>
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 {@Beanpublic 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();}
}

common模块配置类的生效

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 {@Autowiredprivate 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;
}

knife4j


集成了swagger 针对swagger进行封装
可以下载离线文档,可以让别人补全这个文档

在common模块即可

     <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId></dependency>
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

3 gateway

全局过滤器来做JWT校验

与service类似 多少了服务就应该有多少个网关
zjj-leadnews-service\zjj-leadnews-user
zjj-leadnews-gateway\zjj-leadnews-user-gateway
user写成了app 因为我们做的是app 登陆就是用这个app登陆的

<properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId></dependency></dependencies>
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);}
}

生成bootstrap.yml

server:port: 51601
spring:application:name: leadnews-app-gatewaycloud:nacos:discovery:server-addr: 192.168.200.130:8848config:server-addr: 192.168.200.130:8848file-extension: yml

nacos网关跨域设置

spring:cloud:gateway:globalcors:add-to-simple-url-handler-mapping: true #跨域 后期用的corsConfigurations:'[/**]':allowedHeaders: "*" ##允许所以域 都可以访问该问服务allowedOrigins: "*"allowedMethods:- GET- POST- DELETE- PUT- OPTIONroutes:# 平台管理- 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

route路由如何实现

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,会被网关全局过滤器拦截

401统一为认证失败的代码

404 找不到任何东西
传token的方式 因为是在headers拿 所有封装在headers给

监听器 > 过滤器(AOP日志记录) > 拦截器(AOP日志记录) > servlet执行 > 拦截器 > 过滤器 > 监听器
可以说URL是URI(URL是URI的子集)

过滤器

@Component //放入到spring容器才能使得该类生效
@Slf4j
public class AuthorizeFilter implements GlobalFilter, Ordered {@Overridepublic 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)){//返回401response.setStatusCode(HttpStatus.UNAUTHORIZED);//结束请求return response.setComplete();}//token是否有效 之前是用AppJwtUtil.getToken(主键id)得到的tokentry {Claims claims = AppJwtUtil.getClaimsBody(token);//解析完得到的claims然后判断是否过期 -1:有效,0:有效,1:过期,2:过期int result = AppJwtUtil.verifyToken(claims);if(result == 1 || result == 2){//返回401response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}//获取用户id claims里面包含 {"id","userId"} 放到headers中 这样拦截器或者其他就能拿到headers中的idObject userId = claims.get("id");ServerHttpRequest serverHttpRequest = request.mutate().headers(new Consumer<HttpHeaders>() {@Overridepublic 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*/@Overridepublic int getOrder() {return 0;}
}

AppJwtUtil

public class AppJwtUtil {// TOKEN的有效期一天(S) 1*24*60*60private static final int TOKEN_TIME_OUT = 3_600;// 加密KEYprivate static final String TOKEN_ENCRY_KEY = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY";// 最小刷新间隔(S)private static final int REFRESH_TIME = 300;// 生产IDpublic 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());// 需要自动刷新TOKENif((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}}

app前段项目集成




index.html里面的地址有app就会访问下面的地址然后到upstream
反向代理找到网关 反向代理中间件帮后端找到网关服务器

nginx -s reload

自媒体前端搭建 nginx


D:\file\001developer\nginx-1.18.0\conf\leadnews.conf
一个nginx可以访问多了conf文件 可以有多个静态页面
多了一个heima-leadnews-wemedia.conf
重启的话 nginx -s reload


jwt java web token 以及 拦截器和生效 token被全局过滤器拿到然后把id设置到headers请求 然后拦截器拦截


对称加密 密钥都一样 加密解密 TOKEN_ENCRY_KEY都一样
非对称加密 KEY不一样
不可逆加密 token解析不出来token

A.B.C

文件参数 文件上传 heima-file-starter minio工具




heima创建的一个依赖



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 {@Overridepublic 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 {@Overridepublic 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*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {WmThreadLocalUtil.clear();}
}

项目环境 project Environment 自我感觉稳定版本

maven - 3.3.9 (现在学习用的)/ 3.6.1
jdk 1.8
Intellij Idea 2020.1.2
Git

全局异常处理-dao-service-controller-gateway网关工程-集中处理token-登陆模块-获取用户信息-ThreadLocal-组件-项目环境推荐-swagger-knif转发相关推荐

  1. 使用Spring的@Autowired 实现DAO, Service, Controller三层的注入(转)

    简述: 结合Spring和Hibernate进行开发 使用@Autowired实现依赖注入, 实现一个学生注册的功能,做一个技术原型 从DAO(Repository) -> Service -& ...

  2. 实战SSM_O2O商铺_36【商品】商品列表之Dao+Service+Controller层的实现

    文章目录 概述 Dao层 ProductDao.java ProductDao.xml 单元测试 Service层 ProductService.java ProductServiceImpl.jav ...

  3. 实战SSM_O2O商铺_41【前端展示】店铺列表页面Dao+Service+Controller层的实现

    文章目录 概述 Dao层 接口 映射文件 单元测试 Service层 接口方法 单元测试 Controller层 增加 ShopListController 单元测试 Github地址 概述 在完成了 ...

  4. 实战SSM_O2O商铺_39【前端展示】首页轮播图和一级商铺Dao+Service+Controller层的实现

    文章目录 概述 HeadLine Dao层 接口 映射文件 单元测试 HeadLine Service层 接口 实现类 单元测试 ShopCategory Dao层完善 映射文件完善 单元测试 Con ...

  5. Dao,Service,Controller层作用

    1.Dao层:全称Data Access Object.Dao层比较底层,负责与数据库打交道具体到对某个表.某个实体的增删改查 2.Service层:又叫服务层或业务层,封装Dao层的操作,使一个方法 ...

  6. SpringCloud3.0+Sa-token+Gateway网关实现鉴权和token登录拦截功能

    版本: Springboot3.0.5以及对应的Springcloud,SpringcloudAlibaba依赖 nacos 2.2.0,sa-token1.34.0,Mysql8.0 前提: 我这边 ...

  7. controller层要写什么_别再写满屏的try-catch了,真丑,全局异常处理不会吗?

    本文讲得比较细,所以篇幅较长.请认真读完,希望读完后能对统一异常处理有一个清晰的认识. 背景 软件开发过程中,不可避免的是需要处理各种异常,就我自己来说,至少有一半以上的时间都是在处理各种异常情况,所 ...

  8. 别再写满屏的try-catch了,真丑,全局异常处理不会吗?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:巨人大哥 www.cnblogs.com/juren ...

  9. 你这代码写得真丑,满屏的try-catch,全局异常处理不会吗?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 # 背景 软件开发过程中,不可避免的是需要处理各种异常,就我自己来 ...

最新文章

  1. java使用eclipse打jar包_Eclipse对Java项目打Jar包
  2. Web Dynpro Controller
  3. python中unique_一个神奇的Python机器学习交互应用开放框架
  4. 并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法
  5. Windows下删除.svn文件夹的简易方法
  6. Sort HDU5884(二分+多叉哈夫曼树)
  7. java static 块 时机_java的static块执行时机
  8. Linux系统有od程序吗,Linux od命令
  9. 设置IDEA显示Services窗口,方面服务启动关停等操作
  10. 图标字体制作,mock数据
  11. gitlab工作流程简介
  12. Oracle中的函数 之 (wm_concat)
  13. Pycharm中文设置教程
  14. 基于注意力机制的循环网络进行层级性多元标签文本分类
  15. 最新美女COS写真网站整站源码下载+实测可用/带数据
  16. 计算机网络教学仿真平台,网络三维虚拟校园仿真平台构建
  17. 我在 CMU 的八年博士生涯...
  18. 【转载】java版打字练习软件
  19. CSS 样式属性大全
  20. 如何在文字识别软件ABBYY中创建区域模板,处理大量相同内容?

热门文章

  1. DL之LSTM/GRU/CNN:基于tensorflow框架分别利用LSTM/GRU、CNN算法对上海最高气温(数据归一化+构造有监督数据集)实现回归预测案例
  2. 19级爪哇程序设计新手赛(题解)
  3. windbg 断点+单步
  4. 常见Java面试+答案
  5. OpenGL---PBO
  6. 运用简单的超市购物车系统,理解重写equals、hashcode的意义
  7. 应该成为一名系统工程师么?
  8. 高一职上册计算机技能考试,职高数学高一基础模块上册
  9. Semi-Supervised Variational Reasoning for Medical Dialogue Generation翻译
  10. vue+flask微博大数据舆情监控+情感分析可视化系统+爬虫