我终于把它修好了.
首先,我登录了,并使用调试器使代码在某处停止,这样我就可以查找SecurityContextHolder.getContext().getAuthentication().我的身份验证对象的类型为OAuth2AuthenticationToken.我仔细看了一下,决定复制它.
我在一个定制的身份验证管理器中完成了这项工作,并在覆盖的身份验证方法中返回了我的OAuth2AuthenticationToken.
CustomAuthenticationManager.java
@Component
public class CustomAuthenticationManager implements AuthenticationManager {
@Bean
protected PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String token = ((Jwt)authentication.getPrincipal()).getTokenValue();
if (token == null)
throw new BadCredentialsException("Invalid token");
return convertAccessToken(token);
}
public OAuth2AuthenticationToken convertAccessToken(String accessToken){
Jwt decode = Tools.parseToken(accessToken);
List<GrantedAuthority> authorities = new ArrayList<>();
for (String s : ((String[]) decode.getClaims().get("cognito:groups"))) {
authorities.add(new SimpleGrantedAuthority("ROLE_" + s));
}
Map<String, Object> claims = decode.getClaims();
OidcIdToken oidcIdToken = new OidcIdToken(decode.getTokenValue(), decode.getIssuedAt(), decode.getExpiresAt(), claims);
DefaultOidcUser user = new DefaultOidcUser(authorities, oidcIdToken, "email");
return new OAuth2AuthenticationToken(user, authorities, "cognito");
}
}
另外,我还将其放在静态工具中.
public static Jwt parseToken(String accessToken) {
DecodedJWT decode = com.auth0.jwt.JWT.decode(accessToken);
HashMap<String, Object> headers = new HashMap<>();
headers.put("alg", decode.getHeaderClaim("alg").asString());
headers.put("kid", decode.getHeaderClaim("kid").asString());
HashMap<String, Object> claims = new HashMap<>();
decode.getClaims().forEach((k, v) -> {
switch(k){
case "cognito:roles":
case "cognito:groups":
claims.put(k, v.asArray(String.class));
break;
case "auth_time":
case "exp":
case "iat":
claims.put(k, v.asLong());
break;
default:
claims.put(k, v.asString());
break;
}
});
return new Jwt(accessToken, decode.getIssuedAt().toInstant(), decode.getExpiresAt().toInstant(), headers, claims);
}
然后,我创建了两个端点.一个是我的"登录页面",一个是我的过滤器转到的页面.因此,在我的登录页面中,我接受了一个访问令牌,将其存储在会话中,然后重定向到通过筛选器的另一个端点.
TokenLoginController.java
@Component
@RestController
public class TokenLoginController {
@GetMapping(value="/login/token/{token}")
@PermitAll
public void setSession(@PathVariable("token") String token, HttpSession session, HttpServletResponse response) throws IOException {
session.setAttribute("access_token", token);
response.sendRedirect("/login/token");
}
@GetMapping(value="/login/token")
@PermitAll
public void setSession() {
}
}
该筛选器扩展AbstractAuthenticationProcessingFilter,从会话中查找访问令牌,创建OAuth2AuthenticationToken,并使用它进行身份验证.
StickyAuthenticationFilter.java
public class StickyAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public StickyAuthenticationFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) {
super(defaultFilterProcessesUrl);
setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws AuthenticationException, IOException, ServletException {
String access_token = (String)servletRequest.getSession().getAttribute("access_token");
if (access_token != null) {
JwtAuthenticationToken authRequest = new JwtAuthenticationToken(Tools.parseToken(access_token));
return getAuthenticationManager().authenticate(authRequest);
}
throw new RuntimeException("Invalid access token");
}
}
最后,我的SecurityConfig将这一切联系在一起,如下所示:
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends VaadinWebSecurity {
private final ClientRegistrationRepository clientRegistrationRepository;
public SecurityConfig(ClientRegistrationRepository clientRegistrationRepository) {
this.clientRegistrationRepository = clientRegistrationRepository;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/login/token/*", "/login/token").permitAll().and()
.addFilterBefore(new StickyAuthenticationFilter("/login/token", new CustomAuthenticationManager()), BearerTokenAuthenticationFilter.class)
.oauth2ResourceServer(oauth2 -> oauth2.jwt())
.authorizeRequests()
.antMatchers("/user/**")
.authenticated();
super.configure(http);
setOAuth2LoginPage(http, "/oauth2/authorization/cognito");
http.oauth2Login(l -> l.userInfoEndpoint().userAuthoritiesMapper(userAuthoritiesMapper()));
}
@Override
public void configure(WebSecurity web) throws Exception {
// Customize your WebSecurity configuration.
super.configure(web);
}
@Bean
public GrantedAuthoritiesMapper userAuthoritiesMapper() {
return (authorities) -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
Optional<OidcUserAuthority> awsAuthority = (Optional<OidcUserAuthority>) authorities.stream()
.filter(grantedAuthority -> "ROLE_USER".equals(grantedAuthority.getAuthority()))
.findFirst();
if (awsAuthority.isPresent()) {
if (awsAuthority.get().getAttributes().get("cognito:groups") != null) {
mappedAuthorities = ((JSONArray) awsAuthority.get().getAttributes().get("cognito:groups")).stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toSet());
}
}
return mappedAuthorities;
};
}
}