spring项目:
1、pom.xml文件
(1)xml文件被编译
代码中的<include>**/*.properties</include>可以让spring项目中的的properties也被编译,如果写的是 <include>**/*.xml</include>那就是让spring项目中的的xml也被编译。
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<!--所在的目录-->
<includes>
<!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<!--所在的目录-->
<includes>
<!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
2、application.yml配置
(1)端口配置
server:
port: 8080
(2)mysql配置
spring:
datasource:
username:
password:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost/byd
(3)日期格式全局配置
spring:
jackson:
date-format: yyyy-MM-dd
time-zone: GMT+8
(4)相同的Bean可以重复注入
spring:
main:
allow-bean-definition-overriding: true
3、配置类
(1)开启异步请求
@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfig {
/** 核心线程数(默认线程数) */
private int corePoolSize = 10;
/** 最大线程数 */
private int maxPoolSize = 20;
/** 允许线程空闲时间(单位:默认为秒) */
private static final int keepAliveTime = 60;
/** 缓冲队列大小 */
private int queueCapacity = 10;
@Bean
public Executor asyncServiceExecutor(){
log.info("start asyncServiceExecutor");
ThreadPoolTaskExecutor executor=new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(corePoolSize);
//配置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//配置空闲时间
executor.setKeepAliveSeconds(keepAliveTime);
//配置队列大小
executor.setQueueCapacity(queueCapacity);
//配置线程前缀名
executor.setThreadNamePrefix("async-service-");
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
写在需要异步方法上的注解
@Async("asyncServiceExecutor")
(2)开启跨域支持
@Configuration
public class CorsConfig implements WebMvcConfigurer {
// 设置跨域
@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路径
registry.addMapping("/**")
// 设置允许跨域请求的域名
.allowedOriginPatterns("*")
// 设置是否允许cookie
.allowCredentials(true)
// 设置允许的请求的方式
.allowedMethods("GET","POST","DELETE","PUT")
// 设置允许的header的属性
.allowedHeaders("*")
// 设置允许时间
.maxAge(3600);
}
}
(3)开启全局异常处理
1、全局异常处理器
@RestControllerAdvice
public class MyExceptionHandler extends RuntimeException{
//@ExceptionHandler用于设置当前处理器类对应的异常类型
@ExceptionHandler(SystemException.class)
public Result doSystemException(SystemException ex){
//记录日志
//发送消息给运维
//发送邮件给开发人员,ex对象发送给开发人员
return new Result(ex.getCode(),null,ex.getMessage());
}
@ExceptionHandler(BusinessException.class)
public Result doBusinessException(BusinessException ex){
return new Result(ex.getCode(),null,ex.getMessage());
}
//除了自定义的异常处理器,保留对Exception类型的异常处理,用于处理非预期的异常
@ExceptionHandler(Exception.class)
public Result doOtherException(Exception ex){
//记录日志
//发送消息给运维
//发送邮件给开发人员,ex对象发送给开发人员
return new Result(Code.SYSTEM_UNKNOW_ERR,null,"系统繁忙,请稍后再试!");
}
}
2、其它自定义异常
业务异常
public class BusinessException extends RuntimeException{
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
public BusinessException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
系统异常
public class SystemException extends RuntimeException{
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public SystemException(Integer code, String message) {
super(message);
this.code = code;
}
public SystemException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
reids项目:
1、redis.conf
(1)连接配置
# 允许访问的地址,默认是127.0.0.1,会导致只能在本地访问。修改为0.0.0.0则可以在任意IP访问,生产环境不要设置为0.0.0.0 第75行
bind 0.0.0.0
# 守护进程,修改为yes后即可后台运行 第257行
daemonize yes
# 监听的端口
port 6379
# 工作目录,默认是当前目录,也就是运行redis-server时的命令,日志、持久化等文件会保存在这个目录
dir .
# 数据库数量,设置为1,代表只使用1个库,默认有16个库,编号0~15
databases 1
# 设置redis能够使用的最大内存
maxmemory 512mb
# 日志文件,默认为空,不记录日志,可以指定日志文件名
logfile "redis.log"
(2)RDB持久化配置
# 是否压缩 ,建议不开启,压缩也会消耗cpu,磁盘的话不值钱
# 360 行
rdbcompression yes
# RDB文件名称
dbfilename dump.rdb
# 文件保存的路径目录
dir ./
(3)AOF持久化配置
# 是否开启AOF功能,默认是no
# 1251 行
appendonly yes
# AOF文件的名称
appendfilename "appendonly.aof"
# 表示每执行一次写命令,立即记录到AOF文件
appendfsync always
# 写命令执行完先放入AOF缓冲区,然后表示每隔1秒将缓冲区数据写到AOF文件,是默认方案
appendfsync everysec
# 写命令执行完先放入AOF缓冲区,由操作系统决定何时将缓冲区内容写回磁盘
appendfsync no
# AOF文件比上次文件 增长超过多少百分比则触发重写
auto-aof-rewrite-percentage 100
# AOF文件体积最小多大以上才触发重写
auto-aof-rewrite-min-size 64mb
2、application.yml配置
(1)java连接redis
该配置的作用是使java和redis可以连接,主要配置了redis的地址和端口。
spring:
# 配置连接redis的主机地址、端口
redis:
host: 127.0.0.1
port: 6379
(2)java连接redis 哨兵
spring:
redis:
sentinel:
master: mymaster
nodes:
- 192.168.60.129:27001
- 192.168.60.129:27002
- 192.168.60.129:27003
3、配置类
(1)序列化配置
该配置类的作用是让redis中键和值的序列化保持不一致。因为redis中默认键和值的序列化方式是一致的,要么是字符串序列化,要么是对象序列化。但是实际开发中键的数据类型往往是字符串,而值的数据类型往往是对象,因此要在这里配置,使键和值的序列化方式分离避免出现序列化失误的情况。
package com.woniu.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author vampire
* @date 2022-05-26
* @desc
*/
@Configuration
public class RedisConfig {
/**
* 给RedisTemplate配置序列化工具
*
*/
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置key的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
//设置value的序列化
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// Jackson2JsonRedisSerializer 这个完成序列化操作
// 不能把 序列化字符串 转成 java 对象
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
// hash类型key的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// hash类型value的序列化方式
redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
return redisTemplate;
}
}
(2)读写分离配置
该类写在RedisConfig 这个配置类中,配置redis的读写分离,其中包括了四种读写策:(1)MASTER:从主节点读取。(2)MASTER_PREFERRED:优先从master节点读取,master不可用才读取replica。(3)REPLICA:从slave(replica)节点读取。(4)REPLICA _PREFERRED:优先从slave(replica)节点读取,所有的slave都不可用才读取master。
@Bean
public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}
3、访问分片集群
(1)读写分离配置
@Bean
public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}
(2)application.yml配置
spring:
redis:
cluster:
nodes:
- 192.168.60.129:7001
- 192.168.60.129:7002
- 192.168.60.129:7003
- 192.168.60.129:8001
- 192.168.60.129:8002
- 192.168.60.129:8003
Shiro项目:
1、配置类
package com.woniu.config;
import com.woniu.realm.MyRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
/**
* @author vampire
* @date 2022-05-16
* @desc
*/
@Configuration
public class ShiroConfig {
//进行加密的设置
@Bean
public CredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
// 指定加密算法
matcher.setHashAlgorithmName("md5");
// 迭代次数
matcher.setHashIterations(3);
return matcher;
}
// 新建MyRealm对象的方法
@Bean
public MyRealm createMyRealm(CredentialsMatcher credentialsMatcher){
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(credentialsMatcher);
return myRealm;
}
// 新建SecurityManager对象
@Bean
public SecurityManager createSecurityManager(MyRealm myRealm){
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
dwsm.setRealm(myRealm);
return dwsm;
}
// 往容器中丢入过滤器
@Bean
public ShiroFilterFactoryBean createShiroFilterFactoryBean(SecurityManager securityManager){
// 新建ShiroFilterFactoryBean对象
ShiroFilterFactoryBean sffb = new ShiroFilterFactoryBean();
sffb.setSecurityManager(securityManager);
HashMap<String, String> map = new HashMap<>();
// 设置登录页面
sffb.setLoginUrl("/login.html");
// 开放资源
// 1、开发静态资源
map.put("/css/**","anon");
map.put("/img/**","anon");
map.put("/js/**","anon");
map.put("/pages/**","anon");
map.put("/plugins/**","anon");
map.put("/test/**","anon");
// 2、放行登录方法
map.put("/psychological_evaluation/big_and_animal.html","anon");
map.put("/loginServe","anon");
map.put("/userServe","anon");
map.put("/roleServe","anon");
map.put("/container","anon");
// 将所有资源封闭,除了放行的
map.put("/**","authc");
// 将规则放入控制器中
sffb.setFilterChainDefinitionMap(map);
return sffb;
}
/**
* 4、开启权限校验注解支持
*
* aop 的实现 2个方式
*
* jdk
*
* Aspect
*
*/
//5.1 开启Aop自动代理
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator proxy =
new DefaultAdvisorAutoProxyCreator();
// 设置true表示使用子类代理
proxy.setProxyTargetClass(true);
return proxy;
}
//5.2 匹配所有加了权限注解的方法,让权限校验注解生效。注入SecurityManager
@Bean
public AuthorizationAttributeSourceAdvisor advisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor advisor =
new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
2、自定义realm
package com.woniu.realm;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.woniu.bean.Permission;
import com.woniu.bean.RolePermission;
import com.woniu.bean.User;
import com.woniu.bean.UserRole;
import com.woniu.mapper.UserMapper;
import com.woniu.service.IPermissionService;
import com.woniu.service.IRolePermissionService;
import com.woniu.service.IUserRoleService;
import com.woniu.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import java.util.List;
/**
* @author vampire
* @date 2022-05-16
* @desc
*/
@Slf4j
public class MyRealm extends AuthorizingRealm {
@Lazy
@Autowired
IUserService userService;
@Lazy
@Autowired
IUserRoleService userRoleService;
@Lazy
@Autowired
IPermissionService permissionService;
@Lazy
@Autowired
IRolePermissionService rolePermissionService;
@Autowired
UserMapper userMapper;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 拿到User,并拿到uid
User user = (User) principalCollection.getPrimaryPrincipal();
Integer uid = user.getUid();
// 拿到rids
QueryWrapper<UserRole> qw = new QueryWrapper<>();
qw.eq("uid",uid);
qw.select("rid");
List<Object> rids = userRoleService.listObjs(qw);
// 拿到pids
QueryWrapper<RolePermission> rolePermissionWQ = new QueryWrapper<>();
rolePermissionWQ.in("rid",rids);
rolePermissionWQ.select("pid");
List<Object> pids = rolePermissionService.listObjs(rolePermissionWQ);
// 拿到对应的Permission
QueryWrapper<Permission> permissionWQ = new QueryWrapper<>();
permissionWQ.in("pid",pids);
List<Permission> permissions = permissionService.list(permissionWQ);
//为权限对象添加字符串许可
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
for (Permission permission : permissions) {
info.addStringPermission(permission.getPcode());
}
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 拿到前端发送的用户名,authenticationToken是一个Token因此可以拿到用户名
String username = (String)authenticationToken.getPrincipal();
System.out.println("username = " + username);
// 新建查询语句
QueryWrapper<User> qw = new QueryWrapper();
qw.eq("username",username);
// 进行查询
User user = userMapper.selectOne(qw);
// 拿到盐并进行转换
String salt = user.getSalt();
ByteSource bytes = ByteSource.Util.bytes(salt);
// 把数据库中的数据打包到SimpleAuthenticationInfo当中去
AuthenticationInfo info = new SimpleAuthenticationInfo(
user,
user.getPassword(),
bytes,
getName()
);
return info;
}
}
Nginx项目:
nginx主要修改的配置文件是nginx.conf,nginx的三大主要功能是反向代理、负载均衡和动静分离三个方面,因此我从这三个方面对项目配置文件进行介绍。
1、反向代理
(1)单服务器
nginx.conf配置类最主要修改的地方主要有两个,下图绿色框标记的地方是配置nginx主机的地址和端口。而红色框的位置配置的是要反向代理的服务器的位置。
server {
listen 80;
server_name 120.22.22.177;
location /sys1 {
proxy_pass http://120.22.22.133:8080;
}
}
(2)多服务器
多服务器配置与单服务器略有不同。被代理的服务器写在外部,即下图红框外部。并用绿框的代码代替外部红框的代码。当外部有请求时,会先找到nginx的地址,之后再找到被代理服务器的位置,寻找顺序如下图所示。
upstream mysever {
server 120.22.22.133:8080;
server 120.22.22.177:8080;
}
server {
listen 80;
server_name 120.22.22.177;
location / {
proxy_pass http://mysever;
}
}
2、负载均衡
(1)轮巡
upstream mysever {
server 120.22.22.133:8080;
server 120.22.22.177:8080;
}
server {
listen 80;
server_name 120.22.22.177;
location / {
proxy_pass http://mysever;
}
}
(2)权重
weight权重,默认为1,权重越高被分配的客户端越多。指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
upstream mysever {
server 120.22.22.133:8080 weight=1;
server 120.22.22.177:8080 weight=2;
}
server {
listen 80;
server_name 120.22.22.177;
location / {
proxy_pass http://mysever;
}
}
(3)哈希
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
upstream mysever {
ip_hash;
server 120.22.22.133:8080 weight=1;
server 120.22.22.177:8080 weight=2;
}
server {
listen 80;
server_name 120.22.22.177;
location / {
proxy_pass http://mysever;
}
}
(4)第三方
fair(第三方): 在 Nginx 中使用 fair 模块(第三方)来实现负载均衡,fair 采用的不是内建负载均衡使用的轮换的均衡算法,而是可以根据页面大小、响应时间智能的进行负载均衡。按后端服务器的响应时间来分配请求,响应时间短的优先分配。
upstream mysever {
fair;
server 192.168.12.136:8080;
server 192.168.12.136:8081;
}
nginx-upstream-fair-master.zip
3、动静分离
红框的位置配置 nginx内部服务器的位置
upstream mysever {
server 120.22.22.133:8080;
server 120.22.22.177:8080;
}
server {
listen 80;
server_name 120.22.22.177;
location / {
proxy_pass http://mysever;
}
}
docker 项目
1、Dockerfile 文件
FROM centos:7
MAINTAINER he <scott@woniu.cn>
RUN yum install -y vim
WORKDIR /usr
CMD /bin/bash
mybatis-plus项目
1、自动代码生成器
(1)运行类
该类写在启动类的文件夹下。
public class AutoGenerator {
public static void main(String[] args) {
FastAutoGenerator.create(
"jdbc:mysql://localhost/byd",
"1234",
"1234")
// 全局配置
.globalConfig(builder -> {
builder.author("vampire") // 设置作者
.enableSwagger() // 开启 swagger 模式
.outputDir("D:\\code\\java\\smart_sys_baidu\\src\\main\\java"); // 指定输出目录
})
// 包配置
.packageConfig(builder -> {
builder.parent("com.woniu") // 设置父包名
.controller("controller") //controller
.service("service") //service
.serviceImpl("service.impl")
.mapper("mapper")
.xml("mapper")
.entity("bean");
})
//表的配置
.strategyConfig(builder -> {
builder.addInclude("comprehensive"); // 设置需要生成的表名
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
System.out.println("成功了...");
}
}
(2)maven依赖
不添加此依赖会报错。
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
2、日志配置
写在pom.xml文件中作为日志的配置。
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3、常用注解
(1)逻辑删除注解
@TableLogic
(2)日期格式化注解
@JsonFormat(pattern = "yyyy:MM:dd HH:mm:ss", timezone = "GMT+8")
(3)配置主键
@TableId(value = "id", type = IdType.AUTO)
ElasticSearch项目
1、配置类
该配置类保证了java可以连接ES并进行操作。
package com.hk.hotel.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class EsConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
return new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://120.22.22.177:9200")
));
}
}
JWT项目:
1、依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
2、工具类
public class JwtTokenUtils {
// 携带token的请求头名字
public final static String TOKEN_HEADER = "Authorization";
//token的前缀
public final static String TOKEN_PREFIX = "Bearer ";
// 默认密钥
public final static String DEFAULT_SECRET = "abcdefghijklmnoqprstuvwxyz";
// 一分钟
public final static long ONE_MINUTES = 60 * 1000;
// token有效期 30 分钟
public final static long TOKEN_EXPIRATION = 1 * ONE_MINUTES;
// 具体加密的算法
private static final Algorithm algorithm = Algorithm.HMAC256(DEFAULT_SECRET);
// json解析 对象
private static ObjectMapper objectMapper = new ObjectMapper();
/**
* 登录用户
*
* @return
*/
public static String createToken(User user) throws JsonProcessingException {
// 2:创建token
Map<String, Object> headerClaims = new HashMap<>();
// 类型 zip
headerClaims.put("typ", "JWT");
return JWT.create()
// 放进去的目的,方便你获取用户信息,避免查询,最后是去掉,
// 后续可能会根据id去查询数据,实时的获取用户信息
.withClaim("token", objectMapper.writeValueAsString(user))
.withHeader(headerClaims)
// 放一份在主题里
.withSubject(user.getId() + "")
// 签发token作者
.withIssuer(user.getUserName())
//设置签发时间:iat
.withIssuedAt(generateCurrentDate())
//设置过期时间:exp,必须要大于签发时间
.withExpiresAt(generateExpirationDate())
//签名信息,采用secret作为私钥
.sign(algorithm);
}
/**
* 检验token是否正确
* @param token
* @return
*/
public static User parseToken(String token) throws JsonProcessingException {
String tokenInfo = getJWTFromToken(token).getClaim("token").asString();
return objectMapper.readValue(tokenInfo, User.class);
}
/**
* 验证是否token过期
*
* @param token
* @return
*/
public static boolean isverfiy(String token) {
try {
JWTVerifier verifier = JWT.require(algorithm).build();
// 如果token失效了,verify就会出现异常
verifier.verify(token);
return true;
} catch (Exception ex) {
return false;
}
}
/**
* 拿到 jwt存储的数据
* @param token
* @return
*/
public static DecodedJWT getJWTFromToken(String token) {
DecodedJWT jwt;
try {
JWTVerifier verifier = JWT.require(algorithm).build();
jwt = verifier.verify(token);
return jwt;
} catch (Exception ex) {
jwt = null;
log.info("token is expired....");
}
return jwt;
}
/**
* 从请求头中获取token
*
* @param request
* @return
*/
public static String getJwtToken(HttpServletRequest request) {
// 如果cookie中没有,就去header里面获取
String header = request.getHeader(TOKEN_HEADER);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
log.info("请求头不含JWT token, 调用下个过滤器");
return null;
}
//去掉token prefix
String token = header.split(" ")[1].trim();
return token;
}
/**
* 当前时间
*
* @return
*/
private static Date generateCurrentDate() {
return new Date();
}
/**
* 过期时间
*
* @return
*/
private static Date generateExpirationDate() {
return new Date(new Date().getTime() + TOKEN_EXPIRATION);
}
}
注意事项: public final static String TOKEN_PREFIX = "Bearer ";这句代码中Bearer后面是有空格的,这是官方文档让我们这么写的,大家不要忘记了token前缀后面要加一个空格。
3、配置类(拦截器)
(1)拦截器定义
public class JwtInterceptor implements HandlerInterceptor {
/**
* 拦截器 拦截的方法
*
* 01 如果 结果为true 放行 会执行 controller 的对应方法
* 01 如果 结果为false 不放行 不会执行 controller
*
*
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 每次请求 会携带token
//通过 request 来获取
String token = JwtTokenUtils.getJwtToken(request);
if (token == null) {
throw new Exception("判断toten 是否存在...");
}
//isverfiy=false代表过期
boolean isverfiy = JwtTokenUtils.isverfiy(token);
if(!isverfiy){
throw new Exception("判断是否过期...");
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
(2)拦截器注册
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
// 注册
@Bean
public JwtInterceptor jwtInterceptor() {
return new JwtInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 给jwt指定拦截规则 未来以/api/开头的资源接口全部要token鉴权
registry.addInterceptor(jwtInterceptor()).addPathPatterns("/api/**");
}
}