在不同的微服务中读取已经验证的 JWT 令牌
Read the already validated JWT token in a different micro service
我有两个微服务,即网关服务和用户服务。网关服务将登录调用重定向到用户服务。用户服务将生成包含角色和权限等信息的 jwt 令牌,并将其附加到响应 header.
现在客户端可以调用用户服务来创建用户。在请求中传递 jwt 令牌,网关服务验证令牌并将调用转发给用户服务。由于我需要检查登录用户是否具有写入权限(基于@PreAuthorize),我需要在用户服务中再次读取 JWT 令牌并提取角色和权限。
网关服务WebSecurityclass配置方法
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.headers().frameOptions().disable();
http.authorizeRequests()
.antMatchers(HttpMethod.POST, "/user/signup").permitAll()
.antMatchers(HttpMethod.POST, "/user/signin").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new AuthenticationFilter(authenticationManager(), environment));
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
AuthenticationFilter.Class
public class AuthenticationFilter extends BasicAuthenticationFilter{
private final Environment environment;
public AuthenticationFilter(AuthenticationManager authenticationManager, Environment environment) {
super(authenticationManager);
this.environment = environment;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String authHeader = request.getHeader(environment.getProperty("auth.token.header.name"));
if (authHeader == null || !authHeader.startsWith("Bearer")) {
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String authHeader = request.getHeader(environment.getProperty("auth.token.header.name"));
//validate jwt token code
}
在 application.properties 中启用此功能,以便 header 中的 jwt 令牌信息从网关传递到用户服务。
zuul.routes.user-service.sensitive-headers=Cookie,Set-Cookie
用户服务网络安全class配置方法
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/**").hasIpAddress(environment.getProperty("api.gateway.ip"))
.and()
.addFilter(getAuthenticationFilter());
http.headers().frameOptions().disable();
}
private AuthenticationFilter getAuthenticationFilter() throws Exception{
AuthenticationFilter filter = new AuthenticationFilter(usersService, environment, authenticationManager());
filter.setFilterProcessesUrl("/signin");
return filter;
}
UserService AuthenticationFilter class
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationService userService;
private final Environment environment;
@Autowired
public AuthenticationFilter(AuthenticationService userService, Environment environment, AuthenticationManager authManager) {
this.userService = userService;
this.environment = environment;
super.setAuthenticationManager(authManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
try {
LoginRequestModel creds = new ObjectMapper().readValue(request.getInputStream(), LoginRequestModel.class);
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(creds.getEmail(), creds.getPasssword(), new ArrayList<>())
);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
//generate jwt token code
}
}
现在如何读取网关服务发送的 jwt 令牌的值并设置为安全上下文身份验证。我需要再写一个过滤器吗?该过滤器将来自何处。不应为注册或登录执行该过滤器。任何帮助将不胜感激。
根据您的代码,您的第一个过滤器设置为满足 /signin
:
filter.setFilterProcessesUrl("/signin");
现在,您需要第二个过滤器来满足其他所有需求,例如:
.and()
.addFilter(getAuthenticationFilter())
.addFilter(getAuthorizationFilter());
...
@Bean
public AuthorizationFilter getAuthorizationFilter(){
AuthorizationFilter a = new AuthorizationFilter(authenticationManager());
a.setSecret(secret);
return a;
}
例如,您可以:
public class AuthorizationFilter extends BasicAuthenticationFilter {
....
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
//try/catch
String jwt = getJWT(r);
if(jwt != null){
Authentication a = getAuthentication(jwt);
if(a != null){
SecurityContextHolder.getContext().setAuthentication(a);
}
}
chain.doFilter(req, res);
}
private String getJWT(HttpServletRequest r){
String bearerToken = r.getHeader("Authorization");
//Do your checks here
return ...;
}
private Authentication getAuthentication(String jwt){
//Parse the jwt etc
return new UsernamePasswordAuthenticationToken(...);
}
}
关于Zuul,还需要补充:ignoredServices: '*'
我有两个微服务,即网关服务和用户服务。网关服务将登录调用重定向到用户服务。用户服务将生成包含角色和权限等信息的 jwt 令牌,并将其附加到响应 header.
现在客户端可以调用用户服务来创建用户。在请求中传递 jwt 令牌,网关服务验证令牌并将调用转发给用户服务。由于我需要检查登录用户是否具有写入权限(基于@PreAuthorize),我需要在用户服务中再次读取 JWT 令牌并提取角色和权限。
网关服务WebSecurityclass配置方法
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.headers().frameOptions().disable();
http.authorizeRequests()
.antMatchers(HttpMethod.POST, "/user/signup").permitAll()
.antMatchers(HttpMethod.POST, "/user/signin").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new AuthenticationFilter(authenticationManager(), environment));
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
AuthenticationFilter.Class
public class AuthenticationFilter extends BasicAuthenticationFilter{
private final Environment environment;
public AuthenticationFilter(AuthenticationManager authenticationManager, Environment environment) {
super(authenticationManager);
this.environment = environment;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String authHeader = request.getHeader(environment.getProperty("auth.token.header.name"));
if (authHeader == null || !authHeader.startsWith("Bearer")) {
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String authHeader = request.getHeader(environment.getProperty("auth.token.header.name"));
//validate jwt token code
}
在 application.properties 中启用此功能,以便 header 中的 jwt 令牌信息从网关传递到用户服务。
zuul.routes.user-service.sensitive-headers=Cookie,Set-Cookie
用户服务网络安全class配置方法
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/**").hasIpAddress(environment.getProperty("api.gateway.ip"))
.and()
.addFilter(getAuthenticationFilter());
http.headers().frameOptions().disable();
}
private AuthenticationFilter getAuthenticationFilter() throws Exception{
AuthenticationFilter filter = new AuthenticationFilter(usersService, environment, authenticationManager());
filter.setFilterProcessesUrl("/signin");
return filter;
}
UserService AuthenticationFilter class
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationService userService;
private final Environment environment;
@Autowired
public AuthenticationFilter(AuthenticationService userService, Environment environment, AuthenticationManager authManager) {
this.userService = userService;
this.environment = environment;
super.setAuthenticationManager(authManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
try {
LoginRequestModel creds = new ObjectMapper().readValue(request.getInputStream(), LoginRequestModel.class);
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(creds.getEmail(), creds.getPasssword(), new ArrayList<>())
);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
//generate jwt token code
}
}
现在如何读取网关服务发送的 jwt 令牌的值并设置为安全上下文身份验证。我需要再写一个过滤器吗?该过滤器将来自何处。不应为注册或登录执行该过滤器。任何帮助将不胜感激。
根据您的代码,您的第一个过滤器设置为满足 /signin
:
filter.setFilterProcessesUrl("/signin");
现在,您需要第二个过滤器来满足其他所有需求,例如:
.and()
.addFilter(getAuthenticationFilter())
.addFilter(getAuthorizationFilter());
...
@Bean
public AuthorizationFilter getAuthorizationFilter(){
AuthorizationFilter a = new AuthorizationFilter(authenticationManager());
a.setSecret(secret);
return a;
}
例如,您可以:
public class AuthorizationFilter extends BasicAuthenticationFilter {
....
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
//try/catch
String jwt = getJWT(r);
if(jwt != null){
Authentication a = getAuthentication(jwt);
if(a != null){
SecurityContextHolder.getContext().setAuthentication(a);
}
}
chain.doFilter(req, res);
}
private String getJWT(HttpServletRequest r){
String bearerToken = r.getHeader("Authorization");
//Do your checks here
return ...;
}
private Authentication getAuthentication(String jwt){
//Parse the jwt etc
return new UsernamePasswordAuthenticationToken(...);
}
}
关于Zuul,还需要补充:ignoredServices: '*'