Spring Security 应用程序和应用程序内webview之间的SSO

l7mqbcuq  于 2023-01-20  发布在  Spring
关注(0)|答案(1)|浏览(151)

我的用户使用Amazon Cognito通过this plugin登录我的应用。
我也有一个Spring Boot 应用程序用户界面,也由cognito保护。
在我的应用程序流程中的某个点上,我想显示Sping Boot 应用程序的Web视图,让用户配置其他东西。
如何在不让用户再次登录的情况下执行此操作?
如果我创建了一个名为/login/{username}/{password}的端点,使用SecurityContextHolder登录用户并重定向到/home,这是否是一个糟糕的做法?

brc7rcf0

brc7rcf01#

我终于修好了。
首先,我登录,并使用调试器使代码停止在某个地方,以便查找SecurityContextHolder.getContext().getAuthentication()。我的Authentication对象的类型为OAuth2AuthenticationToken。我仔细查看了它,并决定复制它。我在自定义AuthenticationManager中这样做,并在覆盖的authenticationmethod中返回我的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");
    }

}

我也把它放在一个静态的www.example.com中Tools.java

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

相关问题