之前说完了自媒体端,文章端的功能,现在来讲下管理端,管理端主要是对自媒体人提交的文章进行管理,后面几天会描述用户审核,频道管理,媒体审核,敏感词设置,
就网关来说,其实和文章端网关,自媒体端网关一致,无非就是过滤器和jwt功能,还有就是配置文件,nacos配置文件方面的讲究,下面分别上代码
过滤器
@Component
@Slf4j
public class AuthorizeFilter implements Ordered, GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取requesqt和reponse对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//2.判断是否是登陆
if (request.getURI().getPath().contains("/login")){
//放行
return chain.filter(exchange);
}
//3.获取token
String token = request.getHeaders().getFirst("token");
//4.判断token是否存在
if (StringUtils.isBlank(token)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//5.判断token是否有效
try {
Claims claimsBody = AppJwtUtil.getClaimsBody(token);
//是否是过期
int result = AppJwtUtil.verifyToken(claimsBody);
if (result == 1 || result == 2){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//获取用户信息
Object userId = claimsBody.get("id");
//存储header中
ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
httpHeaders.add("userId", userId + "");
}).build();
//重置请求
exchange.mutate().request(serverHttpRequest);
} catch (Exception e) {
e.printStackTrace();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//6.放行
return chain.filter(exchange);
}
/**
* 优先级设置 值越小 优先级越高
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
jwt功能
public class AppJwtUtil {
// TOKEN的有效期一天(S)
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);
long currentTime = System.currentTimeMillis();
return Jwts.builder()
.setId(UUID.randomUUID().toString())
.setIssuedAt(new Date(currentTime)) //签发时间
.setSubject("system") //说明
.setIssuer("heima") //签发者信息
.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信息
*
* @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","11");*/
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"));
}
}
配置文件bootstrap.yml
server:
port: 51603
spring:
application:
name: leadnews-admin-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: admin
uri: lb://leadnews-admin
predicates:
- Path=/admin/**
filters:
- StripPrefix= 1
基本上网关都是千篇一律,没啥好说的,接下来讲一下平台管理登录功能,主要包括登陆业务,拦截器,拦截器的配置,反正按顺序来
管理平台登陆业务实现
@Service
public class AdminUserServiceImpl extends ServiceImpl<AdminUserMapper, AdUser>implements AdminUserService {
@Override
public ResponseResult login(AdLoginDto dto) {
//1.检查参数
if(StringUtils.isBlank(dto.getName()) || StringUtils.isBlank(dto.getPassword())){
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"用户名或密码为空");
}
//2.查询用户
AdUser adUser = getOne(Wrappers.<AdUser>lambdaQuery().eq(AdUser::getName, dto.getName()));
if(adUser == null){
return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
}
//3.比对密码
String salt = adUser.getSalt();
String pswd = dto.getPassword();
pswd = DigestUtils.md5DigestAsHex((pswd + salt).getBytes());
if(pswd.equals(adUser.getPassword())){
//4.返回数据 jwt
Map<String,Object> map = new HashMap<>();
map.put("token", AppJwtUtil.getToken(adUser.getId().longValue()));
adUser.setSalt("");
adUser.setPassword("");
map.put("user",adUser);
return ResponseResult.okResult(map);
}else {
return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
}
}
}
拦截器
@Slf4j
public class AdTokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//得到header中的信息
String userId = request.getHeader("userId");
Optional<String> optional = Optional.ofNullable(userId);
if(optional.isPresent()){
//把用户id存入threadloacl中
AdUser adUser = new AdUser();
adUser.setId(Integer.valueOf(userId));
AdminThreadLocalUtil.setUser(adUser);
log.info("adTokenFilter设置用户信息到threadlocal中...");
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("清理threadlocal...");
AdminThreadLocalUtil.clear();
}
}
拦截器配置文件
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AdTokenInterceptor()).addPathPatterns("/**");
}
}
这里还会用到threadlocal,然后下面是对应的threadlocal
public class AdminThreadLocalUtil {
private final static ThreadLocal<AdUser> AD_USER_THREAD_LOCAL = new ThreadLocal<>();
/**
* 添加用户
* @param wmUser
*/
public static void setUser(AdUser adUser){
AD_USER_THREAD_LOCAL.set(adUser);
}
/**
* 获取用户
*/
public static AdUser getUser(){
return AD_USER_THREAD_LOCAL.get();
}
/**
* 清理用户
*/
public static void clear(){
AD_USER_THREAD_LOCAL.remove();
}
}
这一块的配置文件没啥特别的,和文章,自媒体段的差不多,这里就不上了,有需要的可以在评论区留言
最后,记得一键三连哟,您的支持是我更新的动力,谢谢!