使用gateway的全局过滤器实现单点登录
文章目录一、流程图二、步骤一、流程图二、步骤在springcloud_common中导入JWT依赖代码如下(示例):<!-- JWT --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId></dependen
·
一、流程图
二、举例
在springcloud_common中导入JWT依赖
代码如下(示例):
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<scope>runtime</scope>
</dependency>
.配置yaml
代码如下(示例):secret 自定义
config:
jwt:
secret: ashdjakhsdhaslkdhalsjdlasjdlaksjdlkasjdlasjdlkasdjlasjdaslkdjasl1
expire: 600
JWT的配置类
@Component
public class JWTConfig {
@Value("${config.jwt.secret}") //ashdjakhsdhaslkdhalsjdlasjdlaksjdlkasjdlasjdlkasdjlasjdaslkdjasl1
private String secret;
@Value("${config.jwt.expire}") // 600
private long expire;
/**
* 把指定的 UserDTO 对象 生成 token[jwt]
*/
public String generateJwt(LoginDTO member){
// 加密
byte[] keyBytes = secret.getBytes();
// 获得密钥对象
SecretKey key = Keys.hmacShaKeyFor(keyBytes);
String token = Jwts.builder()
.setHeaderParam("typ", "JWT") //令牌类型
//.setHeaderParam("alg", "HS256") //签名算法
.setIssuedAt(new Date()) //签发时间
.setExpiration(new Date(System.currentTimeMillis() + expire*1000)) //过期时间
.claim("id", member.getId())
.claim("userName", member.getUserName())
.claim("avatarUrl", member.getAvatarUrl())
.claim("roleid",member.getRoleid())
.signWith(key, SignatureAlgorithm.HS256).compact();
return token;
}
/**
* 解析jwt
* @param jwtToken
* @return
*/
public LoginDTO checkJwt(String jwtToken){
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(this.secret.getBytes()).parseClaimsJws(jwtToken);
// map
Claims claims = claimsJws.getBody();
Integer id = claims.get("id",Integer.class);
String userName = claims.get("userName",String.class);
String avatarUrl = claims.get("avatarUrl",String.class);
Integer roleid = claims.get("roleid",Integer.class);
return LoginDTO.builder()
.id(id)
.roleid(roleid)
.avatarUrl(avatarUrl)
.userName(userName)
.build();
}
}
主程序导入JWTconfig结构
写AdminRoleController层
@RestController
@RequestMapping("/admin/role")
@Api(description = "后台管理-角色接口")
public class AdminRoleController {
@Autowired
private RoleServiceImpl roleService;
/**
* 根据 登录用户的角色id 去查询 菜单权限
*/
@GetMapping("/menus")
// @CheckLogin // 加上这个注解 这个方法执行之前 要先判断登录状态
public R getMenuListByRoleid(HttpServletRequest request, @RequestHeader("roleid") Integer roleid){
// 1 获得当前用户的角色id
//Object strroleid = request.getAttribute("roleid");
//Integer roleid = Integer.parseInt(strroleid.toString());
//System.out.println("当前用户的角色id: "+roleid);
// 通过角色id 查询当前角色的菜单的权限
List<MenuDTO> dtos = roleService.findRightByRoleidForMenu(roleid);
return new R(ResponseEnum.SUCCESS,dtos);
}
@GetMapping("/actions/{roleid}")
public List<String> getActions(@PathVariable("roleid") Integer roleid){
return roleService.selectActionRightsByRoleid(roleid);
}
}
RoleServiceImpl层代码
@Service
public class RoleServiceImpl implements RoleServiceI{
/*
*根据角色id获得这个角色的权限,收纳并返回
* */
@Autowired
private RoleMapper roleMapper;
/*
根据角色id找到对应的权限菜单
* */
@Override
public List<MenuDTO> findRightByRoleidForMenu(Integer Roleid){
List<MenuDTO> roots = roleMapper.celectRightByRoleidAndParentid(Roleid, 0);
for (MenuDTO root : roots) {
Integer rightid = root.getRightid();
List<MenuDTO> child = roleMapper.celectRightByRoleidAndParentid(Roleid, rightid);
root.setChildren(child);
}
return roots;
}
@Override
@Cacheable(value="selectActionRightsByRoleid",key="#roleid" )
public List<String> selectActionRightsByRoleid(Integer roleid) {
List<String> strings = roleMapper.selectActionRightsByRoleid(roleid);
return strings;
}
}
提供根据角色id 查询 这个角色拥有的所有能够访问的路径的权限
Mapper
<select id="selectActionRightsByRoleid" parameterType="map" resultType="string">
select b.righturl from role_action_right a
left join action_right b
on a.rightid=b.rightid
where a.roleid=#{roleid}
and b.righttype!=0
</select>
配置Vue到网关的跨域配置类
@Component
public class WebCrossOrigin {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
在gateway中设置全局过滤器检查是否登录
package com.csqf.gateway.filters;
import com.alibaba.fastjson.JSON;
import com.csqf.common.config.JWTConfig;
import com.csqf.common.result.R;
import com.csqf.common.result.ResponseEnum;
import com.csqf.pojo.DTO.LoginDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer;
@Component
public class LoginGlobalFilter implements GlobalFilter, Ordered {
@Autowired
private JWTConfig jwtConfig;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 0 获得访问的路径 如果是login 的请求直接 进入service
String path = exchange.getRequest().getPath().toString();
if(path.equals("/admin/user/login1")){
return chain.filter(exchange);
}
// 1 获得请求头中的token
String jwt = exchange.getRequest().getHeaders().getFirst("X-Token");
// 在过滤器中我们不能获得 下面的方法 上的注解
// 但是你要求的流进
// 2 如果没有jwt 以为没有登录
if(StringUtils.isEmpty(jwt)){
System.out.println("没有登录");
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.OK);
// 改变响应的类型
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
// String fastResult = JsonUtils.objectToJson(new R(ResponseEnum.NO_LOGIN));
String fastResult = JSON.toJSONString(new R(ResponseEnum.HAS_NO_TOKEN,null));
DataBuffer dataBuffer = response.bufferFactory().allocateBuffer().write(fastResult.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(dataBuffer));
}
try {
LoginDTO loginDTO = jwtConfig.checkJwt(jwt);
//向headers中放文件,记得build
//将现在的request 变成 change对象
Consumer<HttpHeaders> httpHeaders = httpHeader -> {
httpHeader.set("roleid", loginDTO.getRoleid()+"");
httpHeader.set("userid", loginDTO.getId()+"");
};
ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate().headers(httpHeaders).build();
exchange.mutate().request(serverHttpRequest).build();
return chain.filter(exchange);
} catch (Exception e) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.OK);
// 改变响应的类型
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
String fastResult = JSON.toJSONString(new R(ResponseEnum.TOKEN_TIMEOUT,null));
DataBuffer dataBuffer = response.bufferFactory().allocateBuffer().write(fastResult.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(dataBuffer));
}
}
@Override
public int getOrder() {
return 0;
}
}
在网关中开启feign客户端
创建伪客户端
@Service
@FeignClient("right-6003")
public interface RightFeignService {
/**
* 根据角色id 查询当前角色的url 集合
*/
@GetMapping("/admin/role/actions/{roleid}")
public List<String> getActions(@PathVariable("roleid") Integer roleid);
}
}
创建全局过滤器【检查当前角色 是否具备权限 访问当前的路径】
@Component
public class ActionCheckGloablFilter implements GlobalFilter, Ordered {
@Autowired
private JWTConfig jwtConfig;
@Autowired
private RightFeignService rightFeignService;
// 过滤逻辑
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1 确定哪些动作权限需要验证
// 1,1 获得访问的路径
String path = exchange.getRequest().getPath().toString();
// 1.2 判断路径
if(!path.endsWith("/check")){
return chain.filter(exchange);
}
//1.3 获得当前的roleid
String jwt = exchange.getRequest().getHeaders().getFirst("X-Token");
LoginDTO loginDTO = jwtConfig.checkJwt(jwt);
Integer roleid = loginDTO.getRoleid();
// 1.4 获得当前角色能够访问的路径的集合
List<String> paths = rightFeignService.getActions(roleid);
// 1.5 是否包含 如果不包含抛出异常
if(!paths.contains(path)){
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.OK);
// 改变响应的类型
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
String fastResult = JSON.toJSONString(new R(ResponseEnum.HAS_NO_RIGHT,null));
DataBuffer dataBuffer = response.bufferFactory().allocateBuffer().write(fastResult.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(dataBuffer));
}
return chain.filter(exchange);
}
// 返回的数字越小 就越先起作用
@Override
public int getOrder() {
return 1;
}
}
这里报错有用到一个配置类FeignConfig
/**
* @description: feign配置
*/
@SpringBootConfiguration
public class FeignConfig {
@Bean
public Decoder feignDecoder() {
return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter()));
}
public ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(new GateWayMappingJackson2HttpMessageConverter());
return new ObjectFactory<HttpMessageConverters>() {
@Override
public HttpMessageConverters getObject() throws BeansException {
return httpMessageConverters;
}
};
}
public class GateWayMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
GateWayMappingJackson2HttpMessageConverter() {
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.valueOf(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8"));
setSupportedMediaTypes(mediaTypes);
}
}
}
更多推荐
已为社区贡献3条内容
所有评论(0)