每次把登录之后把token存入客户端,这样服务端压力小,维护简单;
oauth2登录流程:
1.前端登录请求打过来后台用工具类发送请求,请求token;
2.把token等一系列参数封装成对象返回给前端;
3.前端把token,刷新token,登陆人用户名存入localstore;
4.前端设置前置拦截器,每次发送请求的时候携带token即可;
1.前端登录请求打过来后台用工具类发送请求,请求token;
@Override
public JSONResult toLogin(Login login) {
//1.拼接参数发送请求token
String url = "http://localhost:1050/oauth/token?client_id=admin&client_secret=123456&grant_type=password&redirect_uri=http://www.baidu.com&username=%s&password=%s";
url=String.format(url,login.getUsername(),login.getPassword());
System.out.println(url);
HttpRequest post = HttpUtil.createPost(url);
HttpResponse execute = post.execute();
//2拿到json串
String body = execute.body();
AccessTokenDto dto = JSONObject.parseObject(body, AccessTokenDto.class);
//3.封装token返回给前端
return JSONResult.success(dto);
}
4.前端设置前置拦截器,每次发送请求的时候携带token即可;
//axios前置拦截器,每次请求都会走这里
axios.interceptors.request.use(config => {
//如果已经登录了,每次都把token作为一个请求头传递过程
if (localStorage.getItem('U-TOKEN')) {
// 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
config.headers['Authorization'] = "Bearer " + localStorage.getItem('U-TOKEN');
}
console.debug('config',config)
return config
}, error => {
// Do something with request error
Promise.reject(error)
});
这时已经登录成功了,但此时发送请求的时候我们会看到401,没有权限访问,是因为网关zuul里面会默认屏蔽到敏感的头部信息,我们携带的token就被屏蔽了,此时需要在zuul的yml里设置参数:
zuul:
sensitive-headers:
在yml里面配置完即可携带token访问了;
设置无感刷新token,每次token时间过后请求都会刷新token,再获取一个新的token和刷新的token;
//刷新token
@Override
public JSONResult refreshToken(String refreshToken) {
//通过刷新的token换取新的token
//1.拼接路径
String url = "http://localhost:1050/oauth/token?grant_type=refresh_token&refresh_token=%s&client_id=admin&client_secret=123456";
url=String.format(url,refreshToken);
//2.发送请求
HttpRequest post = HttpUtil.createPost(url);
String body = post.execute().body();
//if(StringUtils.hasLength(body)){
//
//}
//3.封装返回
AccessTokenDto dto = JSONObject.parseObject(body, AccessTokenDto.class);
if(dto.getRefreshToken()==null||dto.getRefreshToken()==""){
return JSONResult.error();
}
return JSONResult.success(dto);
}
前端无感刷新token代码:
//刷新Token
async function getNewToken() {
var refreshToken = localStorage.getItem('R-TOKEN');
if(refreshToken){
return await axios({
url: '/auth/login/refreshToken?refreshToken=' + refreshToken,
method: 'post',
headers: {
'Content-Type':'application/x-www-form-urlencoded'
}
})
}else{
alert("登录失效,请重新登录");
localStorage.clear();
router.replace({ path:"/login" });
}
}
RabbitMQ死信队列?
思路导图:
死信队列,也有交换机,也有队列,也有消费者,和普通消费者没有区别;
RabbitMQ出现死信队列的原因:
1.消费消息的时候出现异常,消息被否认,走catch,走channel.basicNack,进入死信队列;
2.当在队列存活时间超过设置的TTL时间;
3.消费消息的队列超过队列最大长度;
1.消费消息的时候出现异常,消息被否认,走catch,走channel.basicNack,进入死信队列;
@RabbitListener(queues = {"normalQueue"})
public void handleOrderQueue(Message message, Channel channel) throws IOException {
System.out.println("订单消费者类,拿到消息:" + new String(message.getBody()));
try {
//开始处理消息
//模拟处理消息花费时间
//Thread.sleep(6000);
int i = 1/0;//模拟:处理消息时发生了某种异常
//消息处理完毕,一定要记得手动ACK
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
} catch (Exception e){
//发生异常,进行NACK
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
}
}
如果普通消费者出现异常,则会走catch,走死信队列消费;
RabbitListener(queues = {"deadQueue"})
public void handleDeadQueue(Message message, Channel channel) throws IOException {
System.out.println("拿到死信消息:" + new String(message.getBody()));
System.out.println("properties===" + message.getMessageProperties());
/**
* 这里取到消息后,根据实际业务规则,进行处理即可
*/
try {
//模拟处理消息花费时间
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//最后手动ACK
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
2.当在队列存活时间超过设置的TTL时间;
/***********************************************************************************
* 2、发送带有过期时间的订单消息到RabbitMQ
***********************************************************************************/
rabbitTemplate.convertAndSend(
"normalExchange", //普通交换机名称
"normalRoutingKey", //正常路由键
JSONObject.toJSONString(dto));//消息
//new MessagePostProcessor() { //定义消息属性处理类
// @Override
// public Message postProcessMessage(Message message) throws AmqpException {
// MessageProperties messageProperties = message.getMessageProperties();
// /**
// * 设置过期时间TTL(单位:毫秒)
// * 模拟:订单5秒内还未被消费,那么就进入死信队列
// */
// messageProperties.setExpiration("500000");
// //设置持久化
// messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
// return message;
// }
//});
3.消费消息的队列超过队列最大长度;
@Bean
public Queue normalQueue() {
Map<String, Object> arguments = new HashMap<>(2);
/**
* 设置死信相关参数
* x-max-length、x-dead-letter-exchange、x-dead-letter-exchange等参数是固定写法
*/
//设置队列长度
arguments.put("x-max-length", 2);
// 绑定死信交换机
arguments.put("x-dead-letter-exchange", "deadExchange");
// 绑定死信的路由key
arguments.put("x-dead-letter-routing-key", "deadRoutingKey");
return new Queue("normalQueue", true, false,false, arguments);
}
队列存货时间过长或队列长度超过都会进入死信队列;