springcloud如何做权限管理(springsecurity+auth2)?
先看一张脉络图:
现在呢一步一步开始做:
第一步:配置zuul网关
在zuul网关里带上请求头,不做拦截
@Component
@Slf4j
public class AuthFilter extends ZuulFilter {
/**
* 具体的过滤逻辑
* 本例中,检查请求的参数中是否传了token这个参数,如果没传,则请求不被路由到具体的服务实例,
* 直接返回响应,状态码为401
* @return
*/
@Override
public Object run() {
//过滤器过滤
System.out.println("………preHandle……………");
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String header = request.getHeader("Authorization");
if (null != header && !"".equals(header)) {
requestContext.addZuulRequestHeader("header", header);
}
return null;
}
/**
* 表示该过滤器是否过滤逻辑,如果是ture,则执行run()方法;如果是false,则不执行run()方法.
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤顺序,值越小,越早执行该过滤器
* @return
*/
@Override
public int filterOrder() {
// 过滤的顺序
return 0;
}
/**
* Zuul有一下四种过滤器
* "pre":是在请求路由到具体的服务之前执行,这种类型的过滤器可以做安全校验,例如身份校验,参数校验等
* "routing":它用于将请`在这里插入代码片`求路由到具体的微服务实例,在默认情况下,它使用Http Client进行网络请求
* "post":它是在请求已被路由到微服务后执行,一般情况下,用作收集统计信息,指标,以及将响应传输到客户端
* "error":它是在其他过滤器发生错误时执行
*/
@Override
public String filterType() {
return "pre";
}
}
注意:requestContext.addZuulRequestHeader(“header”, header);这句需要避开敏感词汇,例如Authorization等。
第二步:配置具体微服务(两个微服务都差不多,这里以其中一个为例)
①导包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
②写一个类WebSecurityConfig放开所有请求,因为导包后所有请求都不通过了。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests().
antMatchers("/**").
permitAll().
anyRequest().
authenticated().
and().
csrf().
disable();
}
}
③写拦截器配置类,放开部分不需要的拦截:
@Configuration
@EnableWebSecurity
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Autowired
private JwtInterceptor jwtInterceptor;
protected void addInterceptors(InterceptorRegistry registry) {
//注册拦截器要声明拦截器对象和要拦截的请求
System.out.println("进入InterceptorConfig类的addInterceptors方法!");
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**").
excludePathPatterns("/**/login/**").
excludePathPatterns("/**/regist/**").
excludePathPatterns("/**/captcha/**");
}
}
④配置拦截器,做真正核心拦截内容
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("………preHandle……………");
//拦截器只是负责把头请求头中包含token的令牌进行一个解析验证。
if (null == request ||
null == request.getHeader("header") ||
!request.getHeader("header").startsWith("Bearer ")) {
outPutErrorResult(response);
return false;
}
String token = request.getHeader("header").substring(7);
System.out.println("token:" + token);
if (StringUtils.isEmpty(token)) {
outPutErrorResult(response);
return false;
}
try {
Claims claims = jwtUtil.parseJwt(token);
} catch (Exception e) {
e.printStackTrace();
outPutErrorResult(response);
return false;
}
return true;
}
public void outPutErrorResult(HttpServletResponse response) {
ServletOutputStream output = null;
try {
output = response.getOutputStream();
output.write(("{\"status\":\"error\",\"msg\":\"请登录账号@!\",\"code\":\"500\"}").getBytes());
output.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != output) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
这里需要指出,
Claims claims = jwtUtil.parseJwt(token);
之所以可以这么判断token,是因为这里的token生成方式是采用jjwt方式生成的,所以可以按此方式解析。
参看token生成方法:
导包:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
public String createJWT(String id, String subject, String roles) {
long nowMinius = System.currentTimeMillis();
JwtBuilder builder = Jwts.
builder().setId(id).
setSubject(subject).
setIssuedAt(new Date()).
signWith(SignatureAlgorithm.HS256, key).claim("roles", roles);
if (ttl > 0) {
builder.setExpiration(new Date(nowMinius + ttl));
}
return builder.compact();
}
public void setTtl(long ttl) {
this.ttl = ttl;
}
/**
* 解析JWT
*
* @param jwtStr
* @return
*/
public Claims parseJwt(String jwtStr) {
return Jwts.parser().setSigningKey(key).parseClaimsJws(jwtStr).getBody();
}
第三步:写一个类SecuringRequestInterceptor,在微服务相互调用时带上请求头,要不然会被拦截(每个业务微服务都要写)。
@Component
public class SecuringRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
获取上一个请求保存的RequestAttributes,能获取到当前的HttpServletRequest
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
//获取request域
HttpServletRequest request = attributes.getRequest();
//获取头部信息
Enumeration<String> headerNames = request.getHeaderNames();
//把头部信息里面的信息装进去requestTemplate
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
System.out.println("name:"+name);
String values = request.getHeader(name);
System.out.println("values:"+values);
requestTemplate.header(name, values);
}
}
}
}
到这里基本没问题了,
第四步 再写一个抽象类AbstractController.java方便其他类使用用户名用户id用户角色等内容。(非必须)
@Autowired
protected HttpServletRequest request;
@Autowired
private JwtUtil jwtUtil;
public abstract class AbstractController {
@Autowired
protected HttpServletRequest request;
@Autowired
private JwtUtil jwtUtil;
public SysLogUserVo UserInfo(){
SysLogUserVo svuv = new SysLogUserVo();
String header = request.getHeader("header");
String token = header.substring(7);
try {
Claims claims = jwtUtil.parseJwt(token);
svuv.setAccounts(claims.getSubject());
svuv.setId(Integer.parseInt(claims.getId()));
svuv.setRole_name((String) claims.get("roles"));
} catch (Exception e) {
e.printStackTrace();
}
return svuv;
}
}
其他类想获取用户信息只需继承这个类就可以了