基于Session的SSO:
客户端应用的session有效期,控制多长时间跳转一次认证服务器
客户端退出,认证服务器任然保留登录session状态,不会完全退出,前端退出后,要跳转到认证服务器上退出接口同时退出,下次登录认证服务器也需要跳转登录
@component
public class OAuth2LogoutSuccessHandler implements LogoutSuccessHandler{
@override
public void onLogoutSuccess(HttpServletRequest request,HttpServletResponse response){
String redirectUri = request.getParameter("redirect_uri");
if(!StringUtils.isEmpty(redirectUri)){
response.sendRedirect(redirectUri);
}
}
}
在webSecurity中调用:
@Configuration
@EnableWebSecurity
public class OAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private LogoutSuccessHandler logoutSuccessHandler;
...
@Override
protected void configure(HttpSecurity http){
http
.authorizeRequests()
.anyRequest().authenticated()
.adn()
.formLogin().and()
.httpBasic().and()
.logout()
.logoutSuccessHandler(logoutSuccessHandler);
}
}
认证服务器的session有效期,控制多长时间需要用户输入一次用户名密码
在认证服务器中添加session,添加session信息到数据库或Redis中
pom.xml
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-jdbc</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-geode</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
@EnableJdbcHttpSession //存储session在数据库中
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthServerConfig extends AuthorizationServerConfigure{
@Autowired
private AuthenticationManager authenticationManager;
}
application.yml
server:
port:9090
servlet:
session:
timeout: 2592000 //控制session过期时间
Token有效期,控制登录一次能访问多长时间微服务
Token短活,一般session有效,token失效,用户正登录着,token失效了,解决:
注意:和access_token的区别,Refresh token需要和自己应用的ClientId和ClientSecret给授权服务器(可长时间有效),access_token不需要
@Component
public class SessionTokenFilter extends ZuulFilter {
private RestTemplate restTemplate = new RestTemplate();
public String filterType() {
return "pre";
}
public int filterOrder() {
return 0;
}
public boolean shouldFilter() {
return true;
}
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
TokenInfo token = (TokenInfo) request.getSession().getAttribute("token");
if(token!=null){
String tokenValue = token.getAccess_token();
//判断令牌是否过期
if(token.isExpired()){
String oauthServiceUrl = "http://localhost:9070/token/oauth/check_token";
org.springframework.http.HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
httpHeaders.setBasicAuth("admin","123456");
LinkedMultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("grant_type","refresh_token");
params.add("refresh_token",token.getRefresh_token());
HttpEntity<MultiValueMap<String, String>> ent = new HttpEntity<MultiValueMap<String, String>>(params,httpHeaders);
try {
ResponseEntity<TokenInfo> newToken = restTemplate.exchange(oauthServiceUrl, HttpMethod.POST,ent,TokenInfo.class);
request.getSession().setAttribute("token",newToken.getBody().init());
tokenValue = newToken.getBody().getAccess_token();
}catch(Exception e){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(500);
requestContext.setResponseBody("{\"message\":\"refresh fair\"}");
requestContext.getResponse().setContentType("application/json");
}
}
requestContext.addZuulRequestHeader("Authorization","bearer "+tokenValue);
}
return null;
}
}
public class TokenInfo{
private String access_token;
private String refresh_token;
private String token_type;
private Long expires_in;
private String scope;
//过期时间
private LocalDateTime exporeTime;
public void init(){
expireTime = LocalDateTime.now().plusSeconds(expires_in);
}
//判断是否过期
public boolean isExpired(){
return expireTime.isBefore(LocalDataTime.now());
}
}
在AuthServerConfig中需添加userDetailsService
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints){
endpoints
.userDetailsService(userDetailsService) //给refresh_token用的
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
基于Token的SSO
@SpringBootApplication
@RestController
@EnableZuulProxy
public class AdminApplication {
private RestTemplate restTemplate = new RestTemplate();
@PostMapping("/logout")
public void logout(HttpServletRequest request){
request.getSession().invalidate();
}
@PostMapping("/login")
public void login(@RequestBody Credentials credentials, HttpServletRequest request, HttpServletResponse response) throws IOException {
String oauthServiceUrl = "http://localhost:9070/token/oauth/check_token";
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
httpHeaders.setBasicAuth("admin","123456");
LinkedMultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("username",credentials.getUsername());
params.add("password",credentials.getPassword());
params.add("grant_type","password");
params.add("scope","read write");
HttpEntity<MultiValueMap<String, String>> ent = new HttpEntity<MultiValueMap<String, String>>(params,httpHeaders);
ResponseEntity<TokenInfo> token = restTemplate.exchange(oauthServiceUrl, HttpMethod.POST,ent,TokenInfo.class);
request.getSession().setAttribute("token",token.getBody().init());
//基于token的SSO
Cookie accessTokenCookie = new Cookie("xuyu_access_token",token.getBody().getAccess_token());
accessTokenCookie.setMaxAge(token.getBody().getExpires_in().intValue()-3); //有效期
accessTokenCookie.setDomain("xuyu.com"); //所有的域名全部访问cookie的access——token
accessTokenCookie.setPath("/");
response.addCookie(accessTokenCookie);
Cookie refreshTokenCookie = new Cookie("xuyu_refresh_token",token.getBody().getRefresh_token());
refreshTokenCookie.setMaxAge(259200); //有效期
refreshTokenCookie.setDomain("xuyu.com"); //所有的域名全部访问cookie的access——token
refreshTokenCookie.setPath("/");
response.addCookie(refreshTokenCookie);
response.sendRedirect("/");
}
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class,args);
}
}
@Component
public class CookieTokenFilter extends ZuulFilter {
private RestTemplate restTemplate = new RestTemplate();
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
private String getCookie(String name){
String result = null;
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if(StringUtils.equals(name,cookie.getName())){
result = cookie.getValue();
break;
}
}
return result;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
HttpServletResponse response = requestContext.getResponse();
String accessToken = getCookie("xuyu_access_token");
if(StringUtils.isNotBlank(accessToken)){
requestContext.addZuulRequestHeader("Authorization","bearer "+accessToken);
}else{
String refreshToken = getCookie("xuyu_refresh_token");
if(StringUtils.isNotBlank(refreshToken)){
String oauthServiceUrl = "http://localhost:9070/token/oauth/check_token";
org.springframework.http.HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
httpHeaders.setBasicAuth("admin","123456");
LinkedMultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("grant_type","refresh_token");
params.add("refresh_token",refreshToken);
HttpEntity<MultiValueMap<String, String>> ent = new HttpEntity<MultiValueMap<String, String>>(params,httpHeaders);
try {
ResponseEntity<TokenInfo> newToken = restTemplate.exchange(oauthServiceUrl, HttpMethod.POST,ent,TokenInfo.class);
requestContext.addZuulRequestHeader("Authorization","bearer "+newToken.getBody().getAccess_token());
Cookie accessTokenCookie = new Cookie("xuyu_access_token",newToken.getBody().getAccess_token());
accessTokenCookie.setMaxAge(newToken.getBody().getExpires_in().intValue()-3); //有效期
accessTokenCookie.setDomain("xuyu.com"); //所有的域名全部访问cookie的access——token
accessTokenCookie.setPath("/");
response.addCookie(accessTokenCookie);
Cookie refreshTokenCookie = new Cookie("xuyu_refresh_token",newToken.getBody().getRefresh_token());
refreshTokenCookie.setMaxAge(259200); //有效期
refreshTokenCookie.setDomain("xuyu.com"); //所有的域名全部访问cookie的access——token
refreshTokenCookie.setPath("/");
response.addCookie(refreshTokenCookie);
}catch(Exception e){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(500);
requestContext.setResponseBody("{\"message\":\"refresh fair\"}");
requestContext.getResponse().setContentType("application/json");
}
}
}
return null;
}
}
网关的配置大概同上。
退出时也要设置cookie失效:
session和token区别:
基于session的sso:
1.安全,可控制高,跨域 2.复杂度较高,性能低,占用服务器资源 3.适用于百万用户以下和内部管理系统