目前越来越多的系统采用前后端分离技术来进行构建,这类系统需要接入CAS单点登录系统,是否有很好的解决方案呢?肯定有,下面会进行相关接口说明。
前后端分离之后,前端如何保存数据呢?一般前端的本地存储方式有:localStorage、sessionStorage、cookie、webSQL、indexedDB等。这些技术有什么差异和区别,这个不在此展开。目前情况下一般都会采用localStorage、sessionStorage和cookie。具体还要看项目使用场景,建议使用localStorage或是sessionStorage。
其实CAS很早就已经考虑了相关的rest接口,下面我们看一下CAS服务端为我们提供了什么样的rest。具体的实现类为org.jasig.cas.support.rest.TicketsResource,在cas-server-support-rest包下。我们一起来看一下具体的代码实现
@RequestMapping(value = "/v1/tickets", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public final ResponseEntity<String> createTicketGrantingTicket(@RequestBody final MultiValueMap<String, String> requestBody,
final HttpServletRequest request) throws JsonProcessingException {
try (Formatter fmt = new Formatter()) {
//解析入参,用户名和密码
final Credential credential = this.credentialFactory.fromRequestBody(requestBody);
final AuthenticationContextBuilder builder = new DefaultAuthenticationContextBuilder(
this.authenticationSystemSupport.getPrincipalElectionStrategy());
final AuthenticationTransaction transaction =
AuthenticationTransaction.wrap(credential);
this.authenticationSystemSupport.getAuthenticationTransactionManager().handle(transaction, builder);
final AuthenticationContext authenticationContext = builder.build();
//产生tgt
final TicketGrantingTicket tgtId = this.centralAuthenticationService.createTicketGrantingTicket(authenticationContext);
final URI ticketReference = new URI(request.getRequestURL().toString() + '/' + tgtId.getId());
final HttpHeaders headers = new HttpHeaders();
headers.setLocation(ticketReference);
headers.setContentType(MediaType.TEXT_HTML);
//响应数据进行格式化
fmt.format("" );
fmt.format("%s %s", HttpStatus.CREATED, HttpStatus.CREATED.getReasonPhrase())
.format("TGT Created
");
//将tgt返回给客户端
return new ResponseEntity<>(fmt.toString(), headers, HttpStatus.CREATED);
}
可以做个简单的测试,看返回的结果是什么。
curl -v -X POST "http://127.0.0.1:8080/cas/v1/tickets" -d "username=cas&password=mellon"
这时响应到前端的是TGT,这个就是我们一直需要的,TGT信息存储在localStorage中,为后续的访问提供参数。
@RequestMapping(value = "/v1/tickets/{tgtId:.+}", method = RequestMethod.POST, consumes = MediaType
.APPLICATION_FORM_URLENCODED_VALUE)
public final ResponseEntity<String> createServiceTicket(@RequestBody final MultiValueMap<String, String> requestBody,
@PathVariable("tgtId") final String tgtId) {
try {
//获取入参service
final String serviceId = requestBody.getFirst(CasProtocolConstants.PARAMETER_SERVICE);
final AuthenticationContextBuilder builder = new DefaultAuthenticationContextBuilder(
this.authenticationSystemSupport.getPrincipalElectionStrategy());
//通过service创建对象
final Service service = this.webApplicationServiceFactory.createService(serviceId);
final AuthenticationContext authenticationContext =
builder.collect(this.ticketRegistrySupport.getAuthenticationFrom(tgtId)).build(service);
//通过TGT产生对应ST
final ServiceTicket serviceTicketId = this.centralAuthenticationService.grantServiceTicket(tgtId,
service, authenticationContext);
return new ResponseEntity<>(serviceTicketId.getId(), HttpStatus.OK);
进行一个简单URL请求测试:
curl -X POST -v "http://127.0.0.1:8080/cas/v1/tickets/TGT-1-xxxxxxx" -d "service=http://server.test.com"
注意这里的service参数,在请求的时候需要encode。同时建议这里的service地址为后端的地址,这个后端地址是受保护的可以保证后端能够产生session会话信息并响应到前端。
当通过TGT获取到ST之后,就可以直接访问service地址并带带上ST。
http://server.test.com?ticket=ST-1-xxxxx
这个地址其实是前端发起的一个异步请求用于获取到session,可以存入localStorage,并以次来保存与后端的交互。
@RequestMapping(value = "/v1/tickets/{tgtId:.+}", method = RequestMethod.DELETE)
public final ResponseEntity<String> deleteTicketGrantingTicket(@PathVariable("tgtId") final String tgtId) {
this.centralAuthenticationService.destroyTicketGrantingTicket(tgtId);
return new ResponseEntity<>(tgtId, HttpStatus.OK);
}
发起一下请求,请求成功会响应你传递的tgtId。
curl -X DELETE -v "http://127.0.0.1:8080/cas/v1/tickets/TGT-1-xxxxx"
1、前端如何存储TGT以及如何应用后端保持联系
建议使用localStorage或是sessionStorage。
2、如何传递参数
只要注意service的地址以及参数请求头的要求,上面的代码中都有可参考