当前位置: 首页 > 知识库问答 >
问题:

Spring Security OAuth2 AngularJS注销流

樊俊悟
2023-03-14

>

  • OAUTH2服务器使用“authorization_code”授予类型发出带有自动批准的JWT令牌。这有HTML/AngularJS表单来收集用户名/密码。

    ui/webfront-使用@enablesso。它的所有endpoint都是经过身份验证的,即它没有任何未经授权的登陆页面/UI/链接,用户可以点击这些页面进入/UAA服务器。因此点击http://localhost:8080会立即将您重定向到http://localhost:9999/UAA,并显示自定义表单以收集用户名/密码。

    使用上面的方法,我无法计算注销流程。HTTP POST/LOGOUT到UI应用程序会清除UI应用程序中的会话/身份验证,但用户会自动再次登录(因为我已经选择了对所有作用域的自动批准),而不需要再次询问用户名密码。

    查看日志和网络调用,似乎所有的“OAuth舞蹈”都成功地重复了一遍,而不需要再次询问用户用户名/密码,并且似乎身份验证服务器还记得为客户端颁发的最后一个身份验证令牌(使用org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeVices?)。

    我如何告诉身份验证服务器在每次被请求为代码/令牌时请求用户名/密码-无状态。

    或者在我的给定场景中实现注销的最佳方式是什么。

    (若要重新创建,请从UiApplication中删除PermitAll()部分,并在所述启动项目的身份验证服务器中配置AutoApproval。)

    github问题

  • 共有1个答案

    甄坚白
    2023-03-14

    我也遇到了您所描述的错误,并且我看到了一个来自question Spring Boot OAuth2 Single Sign Off的解决方案。我并不是说这是唯一的、全球性的真相解决方案。

    但在这种情况下,

    • 身份验证服务器具有登录表单,您已经从它进行了身份验证
    • 浏览器仍然保持与身份验证服务器的会话
    • 完成注销过程后(吊销令牌、删除Cookie...)然后再次尝试重新登录
    • 身份验证服务器不发送登录表单并自动登录

    客户端(在您的案例中为UI应用程序)应用程序的WebSecurityConfig

    ...
    @Value("${auth-server}/ssoLogout")
    private String logoutUrl;
    @Autowired
    private CustomLogoutHandler logoutHandler;
    ...
        @Override
        public void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http.antMatcher("/**")
                .authorizeRequests()
                .antMatchers("/", "/login").permitAll()
                .anyRequest().authenticated()
            .and()
                .logout()
                    .logoutSuccessUrl(logoutUrl)
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                    .addLogoutHandler(logoutHandler)
            .and()      
                .csrf()
                    .csrfTokenRepository(csrfTokenRepository())
            .and()
                .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
            // @formatter:on
        }
    

    客户端应用程序的自定义注销处理程序

    @Component
    public class CustomLogoutHandler implements LogoutHandler {
    
        private static Logger logger = Logger.getLogger(CustomLogoutHandler.class);
    
        @Value("${auth-server}/invalidateTokens")
        private String logoutUrl;
    
        @Override
        public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
    
            logger.debug("Excution CustomLogoutHandler for " + authentication.getName());
            Object details = authentication.getDetails();
            if (details.getClass().isAssignableFrom(OAuth2AuthenticationDetails.class)) {
    
                String accessToken = ((OAuth2AuthenticationDetails) details).getTokenValue();
                RestTemplate restTemplate = new RestTemplate();
    
                MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
                params.add("access_token", accessToken);
    
                HttpHeaders headers = new HttpHeaders();
                headers.add("Authorization", "bearer " + accessToken);
    
                HttpEntity<Object> entity = new HttpEntity<>(params, headers);
    
                HttpMessageConverter<?> formHttpMessageConverter = new FormHttpMessageConverter();
                HttpMessageConverter<?> stringHttpMessageConverternew = new StringHttpMessageConverter();
                restTemplate.setMessageConverters(Arrays.asList(new HttpMessageConverter[] { formHttpMessageConverter, stringHttpMessageConverternew }));
                try {
                    ResponseEntity<String> serverResponse = restTemplate.exchange(logoutUrl, HttpMethod.POST, entity, String.class);
                    logger.debug("Server Response : ==> " + serverResponse);
                } catch (HttpClientErrorException e) {
                    logger.error("HttpClientErrorException invalidating token with SSO authorization server. response.status code:  " + e.getStatusCode() + ", server URL: " + logoutUrl);
                }
            }
            authentication.setAuthenticated(false);
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            new SecurityContextLogoutHandler().logout(request, response, auth);
    
        }
    
    }
    

    我使用了JDBC tokenStore,所以我需要撤销令牌。在身份验证服务器端,我添加了一个控制器来处理注销进程

    @Controller
    public class AuthenticationController {
    
        private static Logger logger = Logger.getLogger(AuthenticationController.class);
    
        @Resource(name = "tokenStore")
        private TokenStore tokenStore;
    
        @Resource(name = "approvalStore")
        private ApprovalStore approvalStore;
    
        @RequestMapping(value = "/invalidateTokens", method = RequestMethod.POST)
        public @ResponseBody Map<String, String> revokeAccessToken(HttpServletRequest request, HttpServletResponse response, @RequestParam(name = "access_token") String accessToken, Authentication authentication) {
            if (authentication instanceof OAuth2Authentication) {
                logger.info("Revoking Approvals ==> " + accessToken);
                OAuth2Authentication auth = (OAuth2Authentication) authentication;
                String clientId = auth.getOAuth2Request().getClientId();
                Authentication user = auth.getUserAuthentication();
                if (user != null) {
                    Collection<Approval> approvals = new ArrayList<Approval>();
                    for (String scope : auth.getOAuth2Request().getScope()) {
                        approvals.add(new Approval(user.getName(), clientId, scope, new Date(), ApprovalStatus.APPROVED));
                    }
                    approvalStore.revokeApprovals(approvals);
                }
            }
            logger.info("Invalidating access token :- " + accessToken);
            OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(accessToken);
            if (oAuth2AccessToken != null) {
                if (tokenStore instanceof JdbcTokenStore) {
                    logger.info("Invalidating Refresh Token :- " + oAuth2AccessToken.getRefreshToken().getValue());
                    ((JdbcTokenStore) tokenStore).removeRefreshToken(oAuth2AccessToken.getRefreshToken());
                    tokenStore.removeAccessToken(oAuth2AccessToken);
                }
            }
            Map<String, String> ret = new HashMap<>();
            ret.put("removed_access_token", accessToken);
            return ret;
        }
    
        @GetMapping("/ssoLogout")
        public void exit(HttpServletRequest request, HttpServletResponse response) throws IOException {
            new SecurityContextLogoutHandler().logout(request, null, null);
            // my authorization server's login form can save with remember-me cookie 
            Cookie cookie = new Cookie("my_rememberme_cookie", null);
            cookie.setMaxAge(0);
            cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
            response.addCookie(cookie);
            response.sendRedirect(request.getHeader("referer"));
        }
    
    }
    
    http
        .requestMatchers()
            .antMatchers(
            "/login"
            ,"/ssoLogout"
            ,"/oauth/authorize"
            ,"/oauth/confirm_access");
    
     类似资料:
    • 问题内容: 我正在考虑为我的应用程序使用OAuth2。我尝试实现的体系结构如下: 我将拥有自己的(仅此)授权服务器 一些资源应用程序使用授权服务器验证对资源的访问 某些客户端应用程序(网络,移动设备)会将用户重定向到授权服务器进行身份验证,如果成功,则会在资源应用程序上使用api。 到目前为止,我已经成功实现了3个基本应用程序(1个身份验证服务器,1个资源服务器和1个客户端)之间的交互。我无法正常

    • 问题内容: 是否可以检测PC何时注销。我需要开发一个应用程序,在PC注销之前,该应用程序将有关注销时间的文本文档写入文本。 问题答案: 对于.NET,请参见以下问题:[c#中是否有方法可以检测Windows关闭/注销并取消该操作(询问用户之后)http://codingdict.com/questions/159553)

    • 我在Spring Security上遇到了麻烦。我可以登录但不能注销(至少不是按预期)。 登录后,我将被重定向到/secure/home.xhtml 这里是迄今为止的代码: 索引.xhtm spring-security.xhtml, http conf 这是我试图在stackoverflow上实现有关其他答案的注销的方式: 但这两个链接都不起作用。我得到了404。我还读到你应该替换请求。带有pa

    • 我在ADFS上有saml。一切正常,但我有不止一个依赖方的信任。然后,当我登录到我的一个webapp(信赖方信任)并注销时,一切都很好。 但当我登录到第一个web应用程序,然后再登录到第二个web应用程序时,我可以在ADF上使用cookie:samleSession,它结合了两个会话,然后当我从第一个web应用程序注销时,我会重定向到第二个web应用程序上的注销页面,并且不会删除网站1上的cook

    • 我正在用Firebase设置身份验证。我成功登录了,但当我注销时,我的应用程序崩溃了 我在activity_menu上创建菜单。菜单项名为log_out。如果用户单击此项,则必须注销 我尝试这段代码查看错误,但它没有显示给我 我以为它是成功运行的,我没有看到任何错误。我怎么知道呢?

    • void unregister_resource(string name) Use this to dynamically unregister a resource plugin. Pass in the name of the resource. 动态注销一个资源插件,参数是资源的名称。 Example 13-27. unregister_resource 例子 13-27. 注销资源 $sm

    • void unregister_object(string object_name) Use this to unregister an object. 注销一个对象。