springcloud如何做权限管理(springsecurity)

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;
    }
}

其他类想获取用户信息只需继承这个类就可以了

发布了67 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/m0_37635053/article/details/103563571