CAS without CAS login page using restful api and modifiedlogin-webflow.xml
Keith Garry Boyce
garry at consultsure.com
Sat Jan 17 08:07:17 EST 2009
Anyone?
> _____________________________________________
> From: cas-bounces at tp.its.yale.edu
> [mailto:cas-bounces at tp.its.yale.edu] On Behalf Of Keith Garry Boyce
> Sent: Friday, January 16, 2009 9:53 AM
> To: cas at tp.its.yale.edu
> Subject: CAS without CAS login page using restful api and
> modifiedlogin-webflow.xml
>
> I wanted to code a JSF application where the login page is a JSF page
> rather than a CAS page. I see this as a use case that cannot be overlooked
> in present day application development.
>
> I understand that it is discouraged to present credentials to an
> application but an applications security model shouldn't have to be custom
> for every security solution plugged in.
>
> In my case I'm using spring security as the mechanism to glue the security
> stuff together. If I switch from plain security to CAS security it seems
> that all documentation points to me doing custom things for the login page
> such as iframe etc..
>
> So here's what I've come up with. I'd appreciate feedback on this
> solution:
>
> In login-webflow.xml I made it so if ticketGrantingTicketId is passed as a
> parameter on the /cas/login URL then
> Service cookie can be issued directly using the ticketGrantingTicketId. In
> my case I get the ticketGrantingTicketId using the CAS restful api.
>
> <action-state id="initialFlowSetup">
> <action bean="initialFlowSetupAction" />
> <!-- garpinc replace -->
> <!--
> <transition on="success"
> to="ticketGrantingTicketExistsCheck" />
> -->
> <!-- garpinc with -->
> <transition on="success"
> to="ticketGrantingTicketIdExistsCheck" />
> <!-- garpinc end replace -->
> </action-state>
>
> <!-- added by garpinc -->
> <decision-state id="ticketGrantingTicketIdExistsCheck">
> <if test="${requestParameters.ticketGrantingTicketId ==
> null}" then="ticketGrantingTicketExistsCheck"
> else="populateFromRequestParams" />
> </decision-state>
>
> <action-state id="populateFromRequestParams">
> <set attribute="ticketGrantingTicketId"
> value="requestParameters.ticketGrantingTicketId"
> scope="flow"/>
> <transition on="success" to="sendTicketGrantingTicket" />
> </action-state>
> <!-- end of garpinc add -->
>
>
> Then I define a JSF controller. The pseudo steps are as follows
> 1) username/password provided to jsf page
> 2) adaptAuthenticationRequest is called to change this into an
> Authentication CasAuthenticationProvider can process
> 3) user is authenticated using provider
> 4) to participate in SSO getFinalOutcome is called which redirects to
> /cas/login with ticketGrantingTicketId and service which is page you want
> to go to on successful login.
>
> public class CasAuthenticationController extends
> AbstractAuthenticationController {
>
> private static final String CAS_TGR = "CAS_TGR";
> private ServiceProperties serviceProperties;
>
> public void setServiceProperties(ServiceProperties
> serviceProperties) {
> this.serviceProperties = serviceProperties;
> }
>
> private String secureCasURL;
>
> public void setSecureCasURL(String secureCasURL) {
> this.secureCasURL = secureCasURL;
> }
>
> private String ticketURL;
>
> public void setTicketURL(String ticketURL) {
> this.ticketURL = ticketURL;
> }
>
> private boolean useCasSSO = true;
>
> public void setUseCasSSO(boolean useCasSSO) {
> this.useCasSSO = useCasSSO;
> }
>
>
> protected void updateFacesContext(Authentication aut) {
>
> }
>
>
> final protected String getFinalOutcome(String outcome) {
>
> if (outcome.equals("success") && useCasSSO) {
> FacesContext ctx =
> FacesContext.getCurrentInstance();
> ExternalContext extCtx = ctx.getExternalContext();
>
> HttpServletRequest request =
> (HttpServletRequest) extCtx.getRequest();
> String ticket = (String)
> request.getAttribute(CAS_TGR);
>
> try {
> extCtx.redirect(
> secureCasURL
> + "/login?service="
> + request.getScheme() +
> "://" + request.getServerName() + ":" + request.getServerPort() +
> request.getContextPath()
> +
> "/view/secure/facelet/content/home.iface&ticketGrantingTicketId=" +
> ticket);
> // this is ignored because extCtx.redirect
> return "redirect";
> } catch (IOException e) {
> throw new RuntimeException(e);
> }
>
> } else {
> return outcome;
> }
> }
>
> protected Authentication adaptAuthenticationRequest(
> UsernamePasswordAuthenticationToken
> usernamePasswordAuthenticationToken) throws Exception {
>
> HttpClient client = new HttpClient();
>
> PostMethod post = new PostMethod(ticketURL);
> NameValuePair[] usernamePasswordArray = {
> new
> NameValuePair("username",usernamePasswordAuthenticationToken.getPrincipal(
> ).toString()),
> new
> NameValuePair("password",usernamePasswordAuthenticationToken.getCredential
> s().toString())
> };
> post.setRequestBody(usernamePasswordArray);
> int responseCode = client.executeMethod(post);
> if (responseCode != 201) {
> String body = post.getResponseBodyAsString();
> Pattern pattern = Pattern.compile(".*<h3>(.*)</h3>.*",
> Pattern.DOTALL);
> Matcher matcher = pattern.matcher(body);
> if (matcher.matches()) {
> String casError = matcher.group(1);
> throw new AuthenticationServiceException(casError);
> } else {
> throw new RuntimeException("body does not contain an
> error:" + body);
> }
>
> }
> String ticketGrantingResource =
> post.getResponseHeader("Location").getValue();
>
> Pattern pattern = Pattern.compile(".*\\/(.*)");
> Matcher matcher = pattern.matcher(ticketGrantingResource);
> if (matcher.matches()) {
> String ticketGrantingResourceToStore = matcher.group(1);
> // Update cookie in browser for SSO
> HttpServletRequest request =
> (HttpServletRequest)
> FacesContext.getCurrentInstance()
>
> .getExternalContext().getRequest();
> request.setAttribute(CAS_TGR,
> ticketGrantingResourceToStore);
>
> } else {
> throw new RuntimeException("ticketGrantingResource not in
> right format:" + ticketGrantingResource);
> }
>
> post = new PostMethod(ticketGrantingResource);
> NameValuePair[] serviceArray = {
> new
> NameValuePair("service",serviceProperties.getService()),
> };
>
> post.setRequestBody(serviceArray);
> responseCode = client.executeMethod(post);
> if (responseCode != 200) {
> throw new RuntimeException("response code was: " +
> responseCode);
> }
> String ticket = post.getResponseBodyAsString();
>
> return new UsernamePasswordAuthenticationToken(
> CasProcessingFilter.CAS_STATEFUL_IDENTIFIER,
> ticket);
>
> }
>
>
>
> } << File: ATT00037.txt >>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: winmail.dat
Type: application/ms-tnef
Size: 4990 bytes
Desc: not available
Url : http://tp.its.yale.edu/pipermail/cas/attachments/20090117/e1d39015/attachment.bin
More information about the cas
mailing list