参考oauth2 spring-guides项目中的注销流程,一旦用户首次使用用户/密码进行身份验证,下次注销后就不会询问凭据。
如何确保每次退出后都会询问用户名/密码。
这是我想要实现的: -
OAuth2服务器使用" authorization_code"发出JWT令牌授权类型 有自动批准。这有收集的html / angularjs形式 用户名/密码。
UI / Webfront - 使用@EnableSSO。所有端点都经过身份验证 即它没有任何未经授权的登陆页面/ ui /链接该用户 点击进入/ uaa服务器。所以点击http://localhost:8080 立即将您重定向到http://localhost:9999/uaa并呈现 自定义表单以收集用户名/密码。
通过上述方法,我无法进行注销流程。 HTTP POST /注销到UI应用程序会清除UI应用程序中的会话/身份验证,但用户会自动再次登录(因为我选择了自动批准所有范围),而不会再次要求输入用户名密码。
查看日志和网络电话,看起来所有" oauth跳舞"在没有再次要求用户输入用户名/密码的情况下再次成功发生并且似乎auth服务器记住为客户端发出的最后一个auth令牌(使用org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices?)。
如何在每次请求代码/令牌时告诉auth服务器询问用户名/密码 - 无状态。
或者在给定方案中实施注销的最佳方法是什么。
(要稍微重新创建我的要求,请从UiApplication中删除$(document).ready(function() {
$("label").click(function(){
$(this).css("background-color: black;");
});
});
部分,并在上述启动项目的auth服务器中配置permitAll()
。)
答案 0 :(得分:2)
我也遇到了你所描述的错误,我看到了一个问题的解决方案 Spring Boot OAuth2 Single Sign Off。我并不是说这是唯一的全球真理解决方案。
但在场景中,
您需要从身份验证服务器的会话中删除身份验证信息,并将其描述为this。
以下摘录是我如何配置解决方案
客户端(在您的案例中为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"));
}
}
在授权服务器的SecurityConfig中,您可能需要允许此网址为
http
.requestMatchers()
.antMatchers(
"/login"
,"/ssoLogout"
,"/oauth/authorize"
,"/oauth/confirm_access");
我希望这对你有所帮助。
答案 1 :(得分:2)
当您使用JWT令牌时,您无法真正撤销它们。 作为一种解决方法,您可以拥有一个注销休息端点,该端点将存储用于注销调用的时间戳和用户ID。
稍后,您可以将注销时间与JWT令牌发布时间进行比较,并决定是否允许api调用。
答案 2 :(得分:1)
我已经意识到当您从客户端应用程序注销然后以编程方式注销您的authserver时重定向到控制器就可以了。这是我在客户端应用程序上的配置:
@Configuration
@EnableOAuth2Sso
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Value("${auth-server}/exit")
private String logoutUrl;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.logout()
.logoutSuccessUrl(logoutUrl)
.and().authorizeRequests().anyRequest().authenticated();
}
}
这是我在authserver上的配置(只是一个处理/ exit端点的控制器):
@Controller
public class LogoutController {
public LogoutController() {
}
@RequestMapping({"/exit"})
public void exit(HttpServletRequest request, HttpServletResponse response) {
(new SecurityContextLogoutHandler()).logout(request, null, null);
try {
response.sendRedirect(request.getHeader("referer"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Here是一个示例应用,显示了使用JWT的完整实现。检查一下,告诉我们它是否对您有所帮助。