UP! 编辑: 也许我的问题还不够清楚:我的API Web控制器的控制器中的HttpResponse似乎不包含我在MS-Authentication中放入的cookie或Jwt令牌。我认为问题可能出在我在代理接口中声明的方法中。我需要什么样的退货才能使我的回复得到更新?
我有一个微服务“身份验证”,一个带有Zuul的网关和一个Api网站。
我通过Spring Boot安全性实现了jwt令牌安全性。
当我的用户尝试从api网站登录时,用户名和密码将发送到MS-Authentication(Zuul配置为允许任何人调用此MS),并在验证用户在数据库中后生成令牌。
我的问题是我无法获得令牌内部的响应(或cookie,我试图将令牌放入cookie并添加cookie作为响应)。
这是我的第一个带有Spring Boot和微服务的项目!
当我不使用Api并使用postmann进行测试时,令牌可以很好地返回响应!
这是我的代码: Api网站中的LoginController
@Controller
public class LoginController {
private final BookProxy bookProxy;
@Autowired
public LoginController(BookProxy bookProxy) {
this.bookProxy = bookProxy;
}
@GetMapping("/login")
public String loginForm(Model model){
model.addAttribute("user",new UserBean());
return "login";
}
@PostMapping("/login")
public String doLogin(@ModelAttribute UserBean user){
bookProxy.authenticateClient(user);
return "Home";
}
}
Api网络中的代理
@FeignClient(name = "zuul-server", url = "localhost:8762")
public interface Proxy {
/* Login */
@PostMapping("/auth/login")
void authenticateClient(@RequestBody UserBean user);
}
Zuul网关中的SecurityConfig
@EnableWebSecurity
public class SecurityTokenConfig extends WebSecurityConfigurerAdapter {
// Roles
private static final String ADMIN = "ADMIN";
private static final String EMPLOYEE = "EMPLOYEE";
private static final String CLIENT = "CLIENT";
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterAfter(new JwtTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.antMatchers("/book/**").hasAnyRole(ADMIN,EMPLOYEE)
.anyRequest().authenticated();
}
}
MS-Authentication中的SecurityConfig
@EnableWebSecurity
public class SecurityCredentialsConfig extends WebSecurityConfigurerAdapter {
private final UserPrincipalDetailsService userPrincipalDetailsService;
@Autowired
public SecurityCredentialsConfig(UserPrincipalDetailsService userPrincipalDetailsService) {
this.userPrincipalDetailsService = userPrincipalDetailsService;
}
// Roles
private static final String ADMIN = "ADMIN";
private static final String EMPLOYEE = "EMPLOYEE";
private static final String CLIENT = "CLIENT";
@Override
protected void configure(AuthenticationManagerBuilder auth){
auth
.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(new JwtUsernameAndPasswordAuthenticationFilter(authenticationManager()))
.authorizeRequests()
.antMatchers(HttpMethod.POST,"/auth/Login").permitAll()
.antMatchers("/book/consult/**").hasAnyRole(ADMIN,EMPLOYEE)
.antMatchers("/book/**").hasAnyRole(ADMIN,EMPLOYEE)
.anyRequest().authenticated();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
DaoAuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
daoAuthenticationProvider.setUserDetailsService(userPrincipalDetailsService);
return daoAuthenticationProvider;
}
}
对用户进行身份验证的过滤器会在MS-Authentication中生成令牌
public class JwtUsernameAndPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private Logger log = LoggerFactory.getLogger(this.getClass());
// We use auth manager to validate the user credentials
private AuthenticationManager authManager;
JwtUsernameAndPasswordAuthenticationFilter(AuthenticationManager authManager) {
this.authManager = authManager;
// By default, UsernamePasswordAuthenticationFilter listens to "/login" path.
// I use "/auth" path so i need to override the defaults.
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(JwtConfig.URI, "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
// Grab credentials and map them to login viewmodel
LoginViewModel credentials = null;
try {
credentials = new ObjectMapper().readValue(request.getInputStream(), LoginViewModel.class);
} catch (IOException e) {
log.error(e.getMessage());
}
// Create login token
assert credentials != null;
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
credentials.getUsername(),
credentials.getPassword(),
new ArrayList<>());
// Return authenticate user
return authManager.authenticate(authenticationToken);
}
// Upon successful authentication, generate a token.
// The 'auth' passed to successfulAuthentication() is the current authenticated user.
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication auth) throws IOException, ServletException {
// Grab principal
UserPrincipal principal = (UserPrincipal) auth.getPrincipal();
String token = JWT.create()
//.withHeader(headerClaims)
.withClaim("role","ROLE_" + principal.getRole())
.withSubject(principal.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + JwtConfig.EXPIRATION))
.sign(HMAC512(JwtConfig.SECRET.getBytes()));
// ADD COOKIES
Cookie cookie = new Cookie(JwtConfig.HEADER, token);
cookie.setSecure(false);
cookie.setHttpOnly(true);
cookie.setMaxAge(999999);
cookie.setDomain("localhost");
cookie.setPath("/");
// Add token and cookie in response (try both)
response.addHeader(JwtConfig.HEADER, JwtConfig.PREFIX + token);
response.addCookie(cookie);
}
}
Jwt配置常量
public class JwtConfig {
public static final String URI = "/auth/**";
public static final String HEADER = "Authorization";
public static final String PREFIX = "Bearer ";
public static final int EXPIRATION = 24*60*60;
public static final String SECRET = "JwtSecretKey";
}