一。设置移动客户端验证ST通过后,页面不进行302重定向跳转
修改web.xml
二。处理移动端通过ST访问另一个系统比较service通过
1.移动端获取TGT的类,查看源码TicketsResource ,进行修改返回json数据
package org.jasig.cas.support.rest;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.code.kaptcha.Constants;
import com.hivecas.model.ConstantUtils;
import com.hivecas.model.ResponseBean;
import com.hivecas.model.UsernamePasswordStrongCredential;
import org.apache.commons.lang3.StringUtils;
import org.jasig.cas.CasProtocolConstants;
import org.jasig.cas.CentralAuthenticationService;
import org.jasig.cas.authentication.AuthenticationContext;
import org.jasig.cas.authentication.AuthenticationContextBuilder;
import org.jasig.cas.authentication.AuthenticationSystemSupport;
import org.jasig.cas.authentication.AuthenticationException;
import org.jasig.cas.authentication.AuthenticationTransaction;
import org.jasig.cas.authentication.Credential;
import org.jasig.cas.authentication.DefaultAuthenticationContextBuilder;
import org.jasig.cas.authentication.DefaultAuthenticationSystemSupport;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.authentication.principal.ServiceFactory;
import org.jasig.cas.ticket.InvalidTicketException;
import org.jasig.cas.ticket.ServiceTicket;
import org.jasig.cas.ticket.TicketGrantingTicket;
import org.jasig.cas.ticket.registry.DefaultTicketRegistrySupport;
import org.jasig.cas.ticket.registry.TicketRegistrySupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import java.net.URI;
import java.util.Formatter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* {@link RestController} implementation of CAS' REST API.
*
* This class implements main CAS RESTful resource for vending/deleting TGTs and vending STs:
*
*
* - {@code POST /v1/tickets}
* - {@code POST /v1/tickets/{TGT-id}}
* - {@code DELETE /v1/tickets/{TGT-id}}
*
*
* @author Dmitriy Kopylenko
* @since 4.1.0
*/
@RestController("ticketResourceRestController")
public class TicketsResource {
private static final Logger LOGGER = LoggerFactory.getLogger(TicketsResource.class);
@Autowired
@Qualifier("centralAuthenticationService")
private CentralAuthenticationService centralAuthenticationService;
@NotNull
@Autowired(required=false)
@Qualifier("defaultAuthenticationSystemSupport")
private AuthenticationSystemSupport authenticationSystemSupport = new DefaultAuthenticationSystemSupport();
@Autowired(required = false)
private final CredentialFactory credentialFactory = new DefaultCredentialFactory();
@Autowired
@Qualifier("webApplicationServiceFactory")
private ServiceFactory webApplicationServiceFactory;
@Autowired
@Qualifier("defaultTicketRegistrySupport")
private TicketRegistrySupport ticketRegistrySupport = new DefaultTicketRegistrySupport();
private final ObjectMapper jacksonObjectMapper = new ObjectMapper();
/**
* Create new ticket granting ticket.
*
* @param requestBody username and password application/x-www-form-urlencoded values
* @param request raw HttpServletRequest used to call this method
* @return ResponseEntity representing RESTful response
* @throws JsonProcessingException in case of JSON parsing failure
*/
@RequestMapping(value = "/tickets", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseBody
public final ResponseBean createTicketGrantingTicket(@RequestBody final MultiValueMap requestBody,
final HttpServletRequest request) throws JsonProcessingException {
ResponseBean responseBean=new ResponseBean();
try{
final String loginType = requestBody.getFirst("loginType");
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();
final TicketGrantingTicket tgtId = this.centralAuthenticationService.createMobileTicketGrantingTicket(authenticationContext,loginType);
final URI ticketReference = new URI(request.getRequestURL().toString() + '/' + tgtId.getId());
final Map dataMap = new HashMap<>();
dataMap.put("action", ticketReference.toString());
responseBean.setData(dataMap);
responseBean.setStatus(ConstantUtils.response_Status.SUCCESS);
return responseBean;
}
catch(final AuthenticationException e) {
responseBean.setMsg("无权限");
responseBean.setStatus(ConstantUtils.response_Status.NO_AUTH);
return responseBean;
} catch (final BadRequestException e) {
responseBean.setStatus(ConstantUtils.response_Status.FAIL);
LOGGER.error(e.getMessage(), e);
return responseBean;
} catch (final Throwable e) {
responseBean.setStatus(ConstantUtils.response_Status.FAIL);
LOGGER.error(e.getMessage(), e);
return responseBean;
}
}
/**
* Create new service ticket.
*
* @param requestBody service application/x-www-form-urlencoded value
* @param tgtId ticket granting ticket id URI path param
* @return {@link ResponseEntity} representing RESTful response
*/
@RequestMapping(value = "/tickets/{tgtId:.+}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseBody
public final ResponseBean createServiceTicket(@RequestBody final MultiValueMap requestBody,
@PathVariable("tgtId") final String tgtId) {
ResponseBean responseBean=new ResponseBean();
try {
final String serviceId = requestBody.getFirst(CasProtocolConstants.PARAMETER_SERVICE);
final AuthenticationContextBuilder builder = new DefaultAuthenticationContextBuilder(
this.authenticationSystemSupport.getPrincipalElectionStrategy());
final Service service = this.webApplicationServiceFactory.createService(serviceId);
final AuthenticationContext authenticationContext =
builder.collect(this.ticketRegistrySupport.getAuthenticationFrom(tgtId)).build(service);
final ServiceTicket serviceTicketId = this.centralAuthenticationService.grantServiceTicket(tgtId,
service, authenticationContext);
responseBean.setStatus(ConstantUtils.response_Status.SUCCESS);
final Map dataMap = new HashMap<>();
dataMap.put("serviceTicketId", serviceTicketId.getId());
responseBean.setData(dataMap);
return responseBean;
} catch (final InvalidTicketException e) {
responseBean.setStatus(ConstantUtils.response_Status.NO_AUTH);
responseBean.setMsg("TGT不存在");
return responseBean;
} catch (final Exception e) {
LOGGER.error(e.getMessage(), e);
responseBean.setStatus(ConstantUtils.response_Status.FAIL);
return responseBean;
}
}
/**
* Destroy ticket granting ticket.
*
* @param tgtId ticket granting ticket id URI path param
* @return {@link ResponseEntity} representing RESTful response. Signals
* {@link HttpStatus#OK} when successful.
*/
@RequestMapping(value = "/tickets/{tgtId:.+}", method = RequestMethod.DELETE)
@ResponseBody
public final ResponseBean deleteTicketGrantingTicket(@PathVariable("tgtId") final String tgtId) {
this.centralAuthenticationService.destroyTicketGrantingTicket(tgtId);
ResponseBean responseBean=new ResponseBean();
responseBean.setStatus(ConstantUtils.response_Status.SUCCESS);
responseBean.setMsg("删除成功");
return responseBean;
}
public void setAuthenticationSystemSupport(final AuthenticationSystemSupport authenticationSystemSupport) {
this.authenticationSystemSupport = authenticationSystemSupport;
}
public void setWebApplicationServiceFactory(final ServiceFactory webApplicationServiceFactory) {
this.webApplicationServiceFactory = webApplicationServiceFactory;
}
public void setTicketRegistrySupport(final TicketRegistrySupport ticketRegistrySupport) {
this.ticketRegistrySupport = ticketRegistrySupport;
}
public void setCentralAuthenticationService(final CentralAuthenticationService centralAuthenticationService) {
this.centralAuthenticationService = centralAuthenticationService;
}
public CentralAuthenticationService getCentralAuthenticationService() {
return centralAuthenticationService;
}
public AuthenticationSystemSupport getAuthenticationSystemSupport() {
return authenticationSystemSupport;
}
public CredentialFactory getCredentialFactory() {
return credentialFactory;
}
public ServiceFactory getWebApplicationServiceFactory() {
return webApplicationServiceFactory;
}
public TicketRegistrySupport getTicketRegistrySupport() {
return ticketRegistrySupport;
}
/**
* Default implementation of CredentialFactory.
*/
private static class DefaultCredentialFactory implements CredentialFactory {
@Override
public Credential fromRequestBody(@NotNull final MultiValueMap requestBody) {
final String username = requestBody.getFirst("username");
final String password = requestBody.getFirst("password");
final String loginType = requestBody.getFirst("loginType");
if(username == null || password == null||loginType==null) {
throw new BadRequestException("Invalid payload. 'username' and 'password' form fields are required.");
}
if(!StringUtils.equals(loginType, ConstantUtils.loginType.MOBILE)){
throw new BadRequestException("Invalid payload. 'loginType' is wrong.");
}
return new UsernamePasswordStrongCredential(username, password,loginType);
}
}
/**
* Exception to indicate bad payload.
*/
private static class BadRequestException extends IllegalArgumentException {
private static final long serialVersionUID = 6852720596988243487L;
/**
* Ctor.
* @param msg error message
*/
BadRequestException(final String msg) {
super(msg);
}
}
}
2.返回responsebean
package com.hivecas.model;
import java.util.Map;
public class ResponseBean {
//返回状态 1 成功 2 失败
public String status;
//返回数据
public Map data;
//返回信息
public String msg;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Map getData() {
return data;
}
public void setData(Map data) {
this.data = data;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
3.在源码CentralAuthenticationServiceImpl添加createMobileTicketGrantingTicket方法,将移动登录的类型loginType 设置到TGT中
@Audit(
action="TICKET_GRANTING_TICKET",
actionResolverName="CREATE_TICKET_GRANTING_TICKET_RESOLVER",
resourceResolverName="CREATE_TICKET_GRANTING_TICKET_RESOURCE_RESOLVER")
@Timed(name = "CREATE_TICKET_GRANTING_TICKET_TIMER")
@Metered(name = "CREATE_TICKET_GRANTING_TICKET_METER")
@Counted(name="CREATE_TICKET_GRANTING_TICKET_COUNTER", monotonic=true)
public TicketGrantingTicket createMobileTicketGrantingTicket(final AuthenticationContext context,final String loginType)
throws AuthenticationException, AbstractTicketException {
final Authentication authentication = context.getAuthentication();
final TicketGrantingTicketFactory factory = this.ticketFactory.get(TicketGrantingTicket.class);
final TicketGrantingTicket ticketGrantingTicket = factory.create(authentication,loginType);
this.ticketRegistry.addTicket(ticketGrantingTicket);
doPublishEvent(new CasTicketGrantingTicketCreatedEvent(this, ticketGrantingTicket));
return ticketGrantingTicket;
}
4.源码TicketGrantingTicket添加获取getLoginType方法
public interface TicketGrantingTicket extends Ticket {
/** The prefix to use when generating an id for a Ticket Granting Ticket. */
String PREFIX = "TGT";
String getLoginType();
5.这就可以在客户端请求ST票据验证时,不进行service的url比较验证,直接通过。位置在源码CentralAuthenticationServiceImpl类中
@Audit(
action="SERVICE_TICKET_VALIDATE",
actionResolverName="VALIDATE_SERVICE_TICKET_RESOLVER",
resourceResolverName="VALIDATE_SERVICE_TICKET_RESOURCE_RESOLVER")
@Timed(name="VALIDATE_SERVICE_TICKET_TIMER")
@Metered(name="VALIDATE_SERVICE_TICKET_METER")
@Counted(name="VALIDATE_SERVICE_TICKET_COUNTER", monotonic=true)
@Override
public Assertion validateServiceTicket(final String serviceTicketId, final Service service) throws AbstractTicketException {
final RegisteredService registeredService = this.servicesManager.findServiceBy(service);
verifyRegisteredServiceProperties(registeredService, service);
final ServiceTicket serviceTicket = this.ticketRegistry.getTicket(serviceTicketId, ServiceTicket.class);
if (serviceTicket == null) {
logger.info("Service ticket [{}] does not exist.", serviceTicketId);
throw new InvalidTicketException(serviceTicketId);
}
final TicketGrantingTicket root ;
try {
synchronized (serviceTicket) {
if (serviceTicket.isExpired()) {
logger.info("ServiceTicket [{}] has expired.", serviceTicketId);
throw new InvalidTicketException(serviceTicketId);
}
root = serviceTicket.getGrantingTicket().getRoot();
if(root!=null){
String loginType=root.getLoginType();
if(StringUtils.isNotBlank(loginType)||StringUtils.equalsIgnoreCase(loginType, ConstantUtils.loginType.PC)){
if (!serviceTicket.isValidFor(service)) {
logger.error("Service ticket [{}] with service [{}] does not match supplied service [{}]",
serviceTicketId, serviceTicket.getService().getId(), service);
throw new UnrecognizableServiceForServiceTicketValidationException(serviceTicket.getService());
}
}
}
}
// final TicketGrantingTicket root = serviceTicket.getGrantingTicket().getRoot();
final Authentication authentication = getAuthenticationSatisfiedByPolicy(
root, new ServiceContext(serviceTicket.getService(), registeredService));
final Principal principal = authentication.getPrincipal();
final RegisteredServiceAttributeReleasePolicy attributePolicy = registeredService.getAttributeReleasePolicy();
logger.debug("Attribute policy [{}] is associated with service [{}]", attributePolicy, registeredService);
@SuppressWarnings("unchecked")
final Map attributesToRelease = attributePolicy != null
? attributePolicy.getAttributes(principal) : Collections.EMPTY_MAP;
final String principalId = registeredService.getUsernameAttributeProvider().resolveUsername(principal, service);
final Principal modifiedPrincipal = this.principalFactory.createPrincipal(principalId, attributesToRelease);
final AuthenticationBuilder builder = DefaultAuthenticationBuilder.newInstance(authentication);
builder.setPrincipal(modifiedPrincipal);
final Assertion assertion = new ImmutableAssertion(
builder.build(),
serviceTicket.getGrantingTicket().getChainedAuthentications(),
serviceTicket.getService(),
serviceTicket.isFromNewLogin());
doPublishEvent(new CasServiceTicketValidatedEvent(this, serviceTicket, assertion));
return assertion;
} finally {
if (serviceTicket.isExpired()) {
this.ticketRegistry.deleteTicket(serviceTicketId);
}
}
}
@Audit(
action="TICKET_GRANTING_TICKET",
actionResolverName="CREATE_TICKET_GRANTING_TICKET_RESOLVER",
resourceResolverName="CREATE_TICKET_GRANTING_TICKET_RESOURCE_RESOLVER")
@Timed(name = "CREATE_TICKET_GRANTING_TICKET_TIMER")
@Metered(name = "CREATE_TICKET_GRANTING_TICKET_METER")
@Counted(name="CREATE_TICKET_GRANTING_TICKET_COUNTER", monotonic=true)
@Override
public TicketGrantingTicket createTicketGrantingTicket(final AuthenticationContext context)
throws AuthenticationException, AbstractTicketException {
final Authentication authentication = context.getAuthentication();
final TicketGrantingTicketFactory factory = this.ticketFactory.get(TicketGrantingTicket.class);
final TicketGrantingTicket ticketGrantingTicket = factory.create(authentication);
this.ticketRegistry.addTicket(ticketGrantingTicket);
doPublishEvent(new CasTicketGrantingTicketCreatedEvent(this, ticketGrantingTicket));
return ticketGrantingTicket;
}