系统集成了CAS,所以使用Spring Security OAuth2.0时是不能使用password这种方式的,但是可以自定义一种token的生成方式,使用cas_ticket代替password授权方式。
开始之前还请移步 参考我的另一篇博客 https://my.oschina.net/taoyuanping/blog/806601
1、配置xml
1.1 新增授权方式
<oauth2:authorization-server
token-services-ref="defaultTokenServices" client-details-service-ref="clientDetailsService"
user-approval-handler-ref="defaultUserApprovalHandler">
<oauth2:authorization-code />
<oauth2:implicit />
<oauth2:refresh-token />
<oauth2:client-credentials />
<oauth2:password disabled="true" />
<oauth2:custom-grant token-granter-ref="casTicketTokenGranter" />
</oauth2:authorization-server>
新增 <oauth2:custom-grant token-granter-ref="casTicketTokenGranter" />
1.2 配置cas_ticket授权方式
<beans:bean id="defaultOAuth2RequestFactory"
class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
<beans:constructor-arg ref="clientDetailsService" />
</beans:bean>
<beans:bean id="casTicketTokenGranter"
class="net.yakee.framework.security.oauth2.provider.cas.CasTicketTokenGranter">
<beans:constructor-arg index="0" ref="authenticationManager" />
<beans:constructor-arg index="1" ref="defaultTokenServices" />
<beans:constructor-arg index="2" ref="clientDetailsService" />
<beans:constructor-arg index="3" ref="defaultOAuth2RequestFactory" />
</beans:bean>
1.3 配置允许客户端使用cas_ticket方式
<oauth2:client-details-service id="clientDetailsService">
<oauth2:client client-id="123" secret="456" scope="read,write,trust"
redirect-uri="https://www.baidu.com" authorities="ROLE_CLIENT"
authorized-grant-types="authorization_code,refresh_token,implicit,cas_ticket" />
</oauth2:client-details-service>
2、创建CasTicketTokenGranter类
public class CasTicketTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = "cas_ticket";
private final AuthenticationManager authenticationManager;
public CasTicketTokenGranter(AuthenticationManager authenticationManager,
AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
}
protected CasTicketTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
super(tokenServices, clientDetailsService, requestFactory, grantType);
this.authenticationManager = authenticationManager;
}
@Override
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
OAuth2AccessToken token = super.grant(grantType, tokenRequest);
if (token != null) {
DefaultOAuth2AccessToken norefresh = new DefaultOAuth2AccessToken(token);
// The spec says that cas ticket should not be allowed to get a refresh token
norefresh.setRefreshToken(null);
token = norefresh;
}
return token;
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
String username = CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER;
String password = parameters.get(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);
if (password == null) {
throw new InvalidRequestException("A cas ticket must be supplied.");
}
Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
try {
userAuth = authenticationManager.authenticate(userAuth);
}
catch (AccountStatusException ase) {
//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
throw new InvalidGrantException(ase.getMessage());
}
catch (BadCredentialsException e) {
// If the ticket is wrong the spec says we should send 400/invalid grant
throw new InvalidGrantException(e.getMessage());
}
if (userAuth == null || !userAuth.isAuthenticated()) {
throw new InvalidGrantException("Could not authenticate ticket: " + password);
}
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
}
3、配置cas服务器开启REST
3.1 添加依赖
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-support-rest</artifactId>
<version>${cas.version}</version>
</dependency>
使用的是4.2.7版本
3.2 配置web.xml
这个版本的cas webapp 里的 web.xml 已经有rest的配置了
4、测试
4.1 客户端使用用户名密码获取TGT
@Test
public void getCasTgt() {
Map<String, String> params = new HashMap<String, String>();
params.put("username", "admin");
params.put("password", "123456");
String rsp = null;
try {
rsp = WebUtils.doPost("https://cas.yakee.net:8443/cas/v1/tickets", params, 3000, 15000);
System.out.println(rsp);
} catch (Exception e) {
e.printStackTrace();
}
}
返回
<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">
<html>
<head>
<title>201 Created</title>
</head>
<body>
<h1>TGT Created</h1>
<form action="https://cas.yakee.net:8443/cas/v1/tickets/TGT-13-7h2UIaWBwqm9y6OlRC9eCi1xQSmzQMLDQuenopDeXbKFxQnzpt-cas.yakee.net" method="POST">
Service:<input type="text" name="service" value=""><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
4.2 使用TGT获取ticket
@Test
public void getCasTicketByTgt() {
Map<String, String> params = new HashMap<String, String>();
params.put("service", "http://www.yakee.net:8080/framework/login/cas");
String rsp = null;
try {
rsp = WebUtils.doPost("https://cas.yakee.net:8443/cas/v1/tickets/"
+ "TGT-13-7h2UIaWBwqm9y6OlRC9eCi1xQSmzQMLDQuenopDeXbKFxQnzpt-cas.yakee.net", params, 3000, 15000);
System.out.println(rsp);
} catch (Exception e) {
e.printStackTrace();
}
}
返回
ST-34-VUsTHVGKo1ty7hvH3WG0-cas.yakee.net
4.2 使用ticket获取access_token
@Test
public void getTokenByCasTicket() {
Map<String, String> params = new HashMap<String, String>();
params.put("grant_type", "cas_ticket");
params.put("client_secret", "456");
params.put("client_id", "123");
params.put("ticket", "ST-34-VUsTHVGKo1ty7hvH3WG0-cas.yakee.net");
String rsp = null;
try {
rsp = WebUtils.doPost("http://www.yakee.net:8080/framework/oauth/token", params, 3000, 15000);
System.out.println(rsp);
} catch (Exception e) {
e.printStackTrace();
}
}
这里肯定会失败,因为ticket的过期时间只有10s,所以正式环境需要一个解析cas返回的程序。这里只是演示流程。