chatgpt 服务器 windows
Zuul配置OAuth资源服务器
1.分析
我们都知道Zuul是网关,所有的请求都要经过这里,再到指定的资源服务器,但是经过Zuul之后,你携带的token Zuul是不会携带着去访问 指定的资源服务器的,所以会造成一个你携带了token但是还是显示你没有认证
接下来我将展示我的代码,并解释其中的原理
2.
ResourceServerConfig.java
因为所有请求都要经过网关Zuul,所以这里OAuth配置要配置所有的资源服务器以及认证服务器的资源配置,和安全配置
@Configuration public class ResourceServerConfig { @Autowired private TokenStore tokenStore; @Autowired private AuthExceptionEntryPoint authExceptionEntryPoint; //认证服务器的配置 @Configuration @EnableResourceServer public class AuthorizationServer extends ResourceServerConfigurerAdapter{ public String RESOURCE_ID = "zuul"; //资源服务安全配置 @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources .tokenStore(tokenStore)//令牌存储验证服务,让资源服务自己验证token .resourceId(RESOURCE_ID)//资源ID .stateless(true);//会话机制stateless开启 } @Override public void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/uaa/**") .permitAll(); } } //emplyee资源服务器的配置 @Configuration @EnableResourceServer public class ResourceServer extends ResourceServerConfigurerAdapter{ public String RESOURCE_ID = "employee"; //资源服务安全配置 @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources .tokenStore(tokenStore)//令牌存储验证服务,让资源服务自己验证token .resourceId(RESOURCE_ID)//资源ID .authenticationEntryPoint(authExceptionEntryPoint)//配置token异常的处理 .stateless(true);//会话机制stateless开启 } @Override public void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/employee/**").authenticated(); } } /** * ......到时加其他资源服务器 再配置 */ }AuthFilter.java
当请求携带token经过认证 并放入认证对象中,经过此过滤器,把认证对象的信息明文放入转发给资源服务器请求的请求头中
public class AuthFilter extends ZuulFilter { //filter类型 随便写但是要有意义的 @Override public String filterType() { return "pre"; } //数值越小 越优先 @Override public int filterOrder() { return 0; } //是否过滤 @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { RequestContext context = RequestContext.getCurrentContext(); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); //如果不是 OAuth2Authentication 认证对象 则返回null if(!(authentication instanceof OAuth2Authentication)){ return null; } OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication; Authentication userAuthentication = oAuth2Authentication.getUserAuthentication(); //取出用户名 String principal = userAuthentication.getName(); /** * 组装明文token,转发给微服务,放入header,名称为json-token */ final List<String> authorities = new ArrayList<>(); userAuthentication.getAuthorities().stream().forEach(s -> authorities.add(((GrantedAuthority)s).getAuthority())); OAuth2Request oAuth2Request = oAuth2Authentication.getOAuth2Request(); Map<String, String> requestParameters = oAuth2Request.getRequestParameters(); Map<String, Object> jsonToken = new HashMap<>(requestParameters); if (userAuthentication != null) { jsonToken.put("principal",principal); jsonToken.put("authroities",authorities); } try { String jsonStrToken = new ObjectMapper().writeValueAsString(jsonToken); context.addZuulRequestHeader("json-token", Base64.getEncoder().encodeToString(jsonStrToken.getBytes())); } catch (IOException e) { e.printStackTrace(); } return null; } }ZuulConfig.java
因为所有请求都要经过网关Zuul,在这里配置支持跨域请求的配置以及,配置上面的过滤器
@Configuration public class ZuulConfig { @Bean public AuthFilter authFilter(){ return new AuthFilter(); } //支持跨域请求配置 @Bean public FilterRegistrationBean filterRegistrationBean(){ final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowCredentials(true); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); source.registerCorsConfiguration("/**",corsConfiguration); CorsFilter corsFilter = new CorsFilter(source); FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>(corsFilter); filterFilterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); return filterFilterRegistrationBean; } }WebSecurityConfig.java
这里将放行所有请求,把认证的功能交由资源服务器的认证
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/**").permitAll() .and() .csrf().disable() .cors().disable(); } }TokenStoreConfig.java
配置token存储策略和token转换器,以便Zuul将请求头中的token解析出来放入认证对象中
@Configuration public class TokenStoreConfig { private String SIGNING_KEY = "lzj"; @Bean public TokenStore tokenStore(){ //JWT令牌存储方案 return new JwtTokenStore(accessTokenConverter()); } @Bean public JwtAccessTokenConverter accessTokenConverter(){ JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey(SIGNING_KEY); return converter; } }application.yml
添加以下配置信息到配置文件中
#配置下zuul的超时时间,因zuul启用了ribbon的负载均衡,还需要设置ribbon的超时时间,注意ribbon的超时时间要小于zuul超时时间 。 zuul: host: connect-timeout-millis: 15000 #HTTP连接超时要比Hystrix的大 socket-timeout-millis: 60000 #socket超时 #忽略框架默认的服务映射路径 ignored-services: '*' #不忽略任何头部信息,所有header都转发到下游的资源服务器 sensitive-headers: '*' retryable: true #zuul的重试机制开启 需要spring-retry依赖 add-host-header: true #加上主机头信息 routes: oauth-server: serviceId: oauth-server path: /uaa/** employee: serviceId: employee path: /employee/** ribbon: ReadTimeout: 10000 ConnectTimeout: 10000可以看到我的配置文件配置了retryable 重试机制,这需要引入一个依赖
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>如果没有配置重新机制,在如果Zuul访问的资源服务器宕机,他不会触发Hystrix,而是返回错误页面,这不是我们想看到的,这时候你配置了重试机制,他就会重新连接一次,连接失败换其他的资源服务。
到此你还需要到资源服务器配置一个过滤器
这个过滤器的作用是,第一个过滤请求,把请求中请求头的明文认证信息放入到认证对象中,以完成资源服务器的认证
TokenAuthenticationFilter.java
public class TokenAuthenticationFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { String token = httpServletRequest.getHeader("json-token"); if(token != null){ String jsonStrToken = Base64.getDecoder().decode(token).toString(); Map map = new ObjectMapper().readValue(jsonStrToken, Map.class); //用户名 String principal = (String) map.get("principal"); //用户权限 List<String> authroities = (List<String>) map.get("authroities"); String[] authroitiesArray = authroities.toArray(new String[authroities.size()]); //创建authenticationtoken对象 UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(principal, null, AuthorityUtils.createAuthorityList(authroitiesArray)); usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest)); //将对象填充到安全上下文中 SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); } //放行过滤器 filterChain.doFilter(httpServletRequest, httpServletResponse); } }大功告成!!!!
测试