自己写SSO-token验证(三)

回顾

上篇,我们把UUID生成的id,也就是这里的token,放入sso.com的cookie中,但是requestSSOValidateToken方法并没有实现,其实这个方法之前简单的返回boolean型,我们改进一下,让其返回AuthoInfo对象,如果认证通过,返回用户的对象,以便后面取UserName或者UserId之类的需要。

代码修改

sso-client中LoginFilter修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
* @author Liu Hailin
* @create 2017-10-20 下午4:19
**/
public class LoginFilter implements Filter {

private final String USER_SESSION_KEY = "user_ses_key";

private String ssoServerHost;

private String ssoProtocol;

private String ssoLoginContextPath = "/sso/login";
private String ssoAuthContextPath = "/sso/auth";

@Autowired
private RestTemplate restTemplate;

@Override
public void init(FilterConfig filterConfig) throws ServletException {
ssoServerHost = filterConfig.getInitParameter( "sso-server-host" );
ssoProtocol = filterConfig.getInitParameter( "sso-protocol" );
if (StringUtils.isEmpty( ssoProtocol )) {
ssoProtocol = "http";
}
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
if (servletRequest instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;

User user = (User)request.getSession().getAttribute( USER_SESSION_KEY );
if (null != user) {
filterChain.doFilter( servletRequest, servletResponse );
return;
}
String authToken = request.getParameter( "token" );
if (StringUtils.isEmpty( authToken )) {
redirctSSO( request, response );
return;
}
AuthInfo authInfo = requestSSOValidateToken( authToken );
if (null == authInfo || !authInfo.isPass()) {
redirctSSO( request, response );
return;
}
request.getSession().setAttribute( USER_SESSION_KEY, authInfo.getUser() );
}

filterChain.doFilter( servletRequest, servletResponse );
}

private void redirctSSO(HttpServletRequest request, HttpServletResponse response) throws IOException {
String originUrl = request.getRequestURL().toString();
response.sendRedirect(
ssoProtocol + "://" + ssoServerHost + ssoLoginContextPath + "?originUrl=" + originUrl );
}

private AuthInfo requestSSOValidateToken(String authToken) {
ResponseEntity<AuthInfo> infoResponseEntity = restTemplate.getForEntity(
ssoProtocol + "://" + ssoServerHost + ssoAuthContextPath + "?authToken={authToken}", AuthInfo.class,
authToken );
return infoResponseEntity.getBody();
}

@Override
public void destroy() {

}
}

注:RestTemplate用法请参考Spring RestTemplate

sso-server中的LoginController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
* @author Liu Hailin
* @create 2017-10-20 下午3:30
**/
@Controller
@Slf4j
@RequestMapping("/sso")
public class LoginController {

@Autowired
private HttpServletRequest request;

@Autowired
private HttpServletResponse response;

private static String USER_TOKEN = "user_token";

@GetMapping("/auth")
@ResponseBody
public AuthInfo auth(@RequestParam(value = "authToken") String authToken) throws IOException {
AuthInfoBuilder authInfoBuilder = AuthInfo.builder();

authInfoBuilder.token( authToken );

User user = LoginStatusCache.getUserByToken( authToken );

if(null != user){
authInfoBuilder.isPass( true );
authInfoBuilder.user( user );
}

return authInfoBuilder.build();
}

@GetMapping("/login")
public String login(ModelMap map, @RequestParam(value = "originUrl") String originUrl)
throws IOException {

map.put( "originUrl", originUrl );

String authToken = CookieUtil.getCookieByName( request, "simple_sso" );

if (StringUtils.isEmpty( authToken )) {
return "login";
}

String cachedToken = (String)request.getSession().getAttribute( USER_TOKEN );
if (!authToken.equals( cachedToken )) {
return "login";
}

return "redirect:"+originUrl + "?token=" + authToken ;
}

@PostMapping("/login")
public String login(@ModelAttribute User user, ModelMap map, @RequestParam(value = "originUrl") String originUrl)
throws IOException {

log.info( "User [{}] is login success.", user.getUserName() );

//TODO 默认所有用户登录成功
String token = UUID.randomUUID().toString().replace( "-", "" );

LoginStatusCache.addUser( token,user);

Cookie tokenCookie = new Cookie( "simple_sso", token );

tokenCookie.setDomain( "sso.com" );
tokenCookie.setHttpOnly( true );
tokenCookie.setPath( "/" );
tokenCookie.setMaxAge( 10 * 60 );

request.getSession().setAttribute( USER_TOKEN, token );

response.addCookie( tokenCookie );

return "redirect:"+ originUrl + "?token=" + token ;
}
}

其中LoginStatusCache提供了用户认证的必要机制,就是通过token获取User的过程,因为我们是用UUID生成的token。

引入Token生成算法

  • UUID,上面我们使用的一种简单有效的方式
  • DES 或者 AES 对称加密,对UserId,UserName等有意义字段进行一定组合后加密存成token
  • RSA、DSA 非对称,就没法解出,方式跟UUID类似。
  • SHA 散列
  • Bcrypt 性能比较差
  • JWT (JSON WEB TOKEN)
坚持技术分享,您的支持将鼓励我继续创作!