SpringCloud(五) 微服务安全实战 基于session和Token的sso

基于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.适用于百万用户以下和内部管理系统

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值