【问题标题】:How to logout oauth2 client in Spring?如何在 Spring 中注销 oauth2 客户端?
【发布时间】:2018-10-25 13:39:45
【问题描述】:

我有最简单的 oauth2 客户端:

@EnableAutoConfiguration
@Configuration
@EnableOAuth2Sso
@RestController
public class ClientApplication {

    @RequestMapping("/")
    public String home(Principal user, HttpServletRequest request, HttpServletResponse response) throws ServletException {       
        return "Hello " + user.getName();
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(ClientApplication.class)
                .properties("spring.config.name=application").run(args);
    }

}

我还有以下application.yml

server:
  port: 9999
  servlet:
    context-path: /client
security:
  oauth2:
    client:
      client-id: acme
      client-secret: acmesecret
      access-token-uri: http://localhost:8080/oauth/token
      user-authorization-uri: http://localhost:8080/oauth/authorize
    resource:
      user-info-uri: http://localhost:8080/me

logging:
  level:
    org.springframework.security: DEBUG
    org.springframework.web: DEBUG

这是完整的代码。我没有任何额外的源代码。它工作正常。

但现在我想添加一个注销功能。我添加了一个端点,但它不起作用。我尝试执行以下操作:

@RequestMapping("/logout")
    public void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        authentication.setAuthenticated(false);
        new SecurityContextLogoutHandler().logout(request,response,authentication);
        SecurityContextHolder.clearContext();
        request.logout();
        request.getSession().invalidate();
    }

但我仍然登录并且可以访问/ url,它会用用户名回复我。

你能帮我解决这个问题吗?

更新

我尝试了这里描述的方法https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_logout

@EnableAutoConfiguration
@Configuration
@EnableOAuth2Sso
@Controller
public class ClientApplication extends WebSecurityConfigurerAdapter {
    private Logger logger = LoggerFactory.getLogger(ClientApplication.class);

    @RequestMapping("/hello")
    public String home(Principal user, HttpServletRequest request, HttpServletResponse response, Model model) throws ServletException {
        model.addAttribute("name", user.getName());
        return "hello";
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http.antMatcher("/**")
                .authorizeRequests()
                .antMatchers( "/login**", "/webjars/**", "/error**").permitAll()
                .anyRequest()
                .authenticated()
                .and().logout().logoutSuccessUrl("/").permitAll()
                .and()
                    .csrf()
                    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
        // @formatter:on
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(ClientApplication.class)
                .properties("spring.config.name=application").run(args);
    }
}

我在 FE 上写道:

<script type="text/javascript">
        $.ajaxSetup({
            beforeSend: function (xhr, settings) {
                if (settings.type == 'POST' || settings.type == 'PUT'
                    || settings.type == 'DELETE') {
                    if (!(/^http:.*/.test(settings.url) || /^https:.*/
                            .test(settings.url))) {
                        // Only send the token to relative URLs i.e. locally.
                        xhr.setRequestHeader("X-XSRF-TOKEN",
                            Cookies.get('XSRF-TOKEN'));
                    }
                }
            }
        });
        var logout = function () {
            $.post("/client/logout", function () {
                $("#user").html('');
                $(".unauthenticated").show();
                $(".authenticated").hide();
            });
            return true;
        };
        $(function() {
            $("#logoutButton").on("click", function () {
                logout();
            });
        });

    </script>

<input type="button" id="logoutButton" value="Logout"/>

但它仍然不起作用。它会导致以下行为:

发布http://localhost:9999/client/logout 重定向到http://localhost:9999/client 但此页面不存在

github上的源代码:
客户端 - https://github.com/gredwhite/logour_social-auth-client(使用 localhost:9999/client/hello 网址)
服务器 - https://github.com/gredwhite/logout_social-auth-server

【问题讨论】:

  • Post http://localhost:9999/client/logout 重定向到http://localhost:9999/client 但是这个页面不存在 什么问题?只需为http://localhost:9999/client 添加一个页面。或者配置另一个页面而不是logoutSuccessUrl("/")

标签: java spring-security oauth-2.0 spring-security-oauth2 spring-oauth2


【解决方案1】:

您可能希望使用 Spring Security 对 /logout 端点的内置支持,这将做正确的事情(清除会话并使 cookie 无效)。要配置端点,请在我们的 WebSecurityConfigurer 中扩展现有的 configure() 方法:

@Override
protected void configure(HttpSecurity http) throws Exception {
  http.antMatcher("/**")
     .and().logout().logoutSuccessUrl("/").permitAll();
}

【讨论】:

    【解决方案2】:

    尝试将注销 url 添加到您的安全配置中。

        .logout()
            .logoutUrl("/logout")
            .logoutSuccessUrl("/")
            .permitAll();
    

    【讨论】:

      【解决方案3】:

      将以下代码 sn-p 添加到您的 ClientApplication 类。这也将清除您的会话详细信息。

      将以下代码替换为您的网络安全适配器类的配置方法。

      @Override
          protected void configure(HttpSecurity http) throws Exception {
              http.antMatcher("/**")
                      .authorizeRequests()
                      .antMatchers( "/login**", "/webjars/**", "/error**").permitAll()
                      .anyRequest()
                      .authenticated()
                      .and().logout().invalidateHttpSession(true)
                      .clearAuthentication(true).logoutSuccessUrl("/login?logout").deleteCookies("JSESSIONID").permitAll().and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
          }
      

      【讨论】:

        【解决方案4】:

        您可以将帖子更改为 获取http://localhost:9999/client/logout

        对我有用

        【讨论】:

          【解决方案5】:

          您可以从数据库中删除刷新令牌以及访问令牌以节省空间。

          @PostMapping("/oauth/logout")
          public ResponseEntity<String> revoke(HttpServletRequest request) {
              try {
                  String authorization = request.getHeader("Authorization");
                  if (authorization != null && authorization.contains("Bearer")) {
                      String tokenValue = authorization.replace("Bearer", "").trim();
          
                      OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
                      tokenStore.removeAccessToken(accessToken);
          
                      //OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(tokenValue);
                      OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
                      tokenStore.removeRefreshToken(refreshToken);
                  }
              } catch (Exception e) {
                  return ResponseEntity.badRequest().body("Invalid access token");
              }
          
              return ResponseEntity.ok().body("Access token invalidated successfully");
          }
          

          注销的 URL 将是:http://localhost:9999/oauth/logout 此外,在 Authorization 标头中传递访问令牌,如

          授权:承载0cb72897-c4f7-4f01-aed9-2f3f79a75484

          其中,0cb72897-c4f7-4f01-aed9-2f3f79a75484 是访问令牌。

          由于它的 Spring 安全性,不要忘记从授权访问中绕过 /oauth/logout url,因为

          public void configure(WebSecurity web) throws Exception {
              web.ignoring().antMatchers("/hello", "/oauth/logout");
          }
          

          希望它能解决您在 Springboot2+Oauth2 中的注销问题。它对我有用。

          【讨论】:

            猜你喜欢
            • 2018-12-27
            • 1970-01-01
            • 2018-10-26
            • 2017-03-26
            • 2015-05-30
            • 2019-10-14
            • 2012-10-22
            • 1970-01-01
            • 2021-08-15
            相关资源
            最近更新 更多