原文地址,转载请注明出处: https://blog.csdn.net/qq_34021712/article/details/81544181 ©王赛超
我们之前在cas服务端整合了shiro,在shirorealm中通过查询数据库获取用户信息和角色等信息,如果都是内部服务,将从数据库中获取信息改为通过调用接口获取信息就可以了,这也是一种方式,这里是演示一下官网的配置。
cas服务端通过调用其他服务接口,将用户名和密码传过去进行认证。这就是rest认证。
在不允许cas服务直接访问账号数据库的时候,这个时候就需要用到Rest认证。
https://apereo.github.io/cas/5.3.x/installation/Rest-Authentication.html
节点 | 服务名 |
---|---|
app1.cas.com:8081 | CAS客户端(只是用来测试单点登录用的,代码参考之前博客) |
https://server.cas.com:8443/cas | CAS服务端 |
rest.cas.com:8083 | rest-client(认证服务) |
当用户点击登录后,cas会发送post请求到http://rest.cas.com:8083/login
并且把用户信息以"用户名:密码"
进行Base64编码放在authorization请求头中。
若输入用户名密码为:admin/123456;那么请求头包括:
authorization=Basic Base64(admin+MD5(123))
那么发送后客户端必须响应一下数据,cas明确规定如下:
● cas 服务端会通过post请求,并且把用户信息以”用户名:密码”进行Base64编码放在authorization请求头中
● 200状态码:并且格式为
{“@class”:”org.apereo.cas.authentication.principal.SimplePrincipal”,”id”:”casuser”,”attributes”:{}}是成功的
● 403状态码:用户不可用
● 404状态码:账号不存在
● 423状态码:账户被锁定
● 428状态码:过期
● 其他登录失败
<dependency>
<groupId>org.apereo.casgroupId>
<artifactId>cas-server-support-rest-authenticationartifactId>
<version>${cas.version}version>
dependency>
cas.authn.rest.uri=http://rest.cas.com:8083/login
#如果密码有加密,打开下面配置,我的是明文
#cas.authn.rest.passwordEncoder.type=DEFAULT
#cas.authn.rest.passwordEncoder.characterEncoding=UTF-8
#cas.authn.rest.passwordEncoder.encodingAlgorithm=MD5
rest-client为一个传统的ssm项目,提供一个rest接口,接口遵循上面介绍的规则。这里这展示接口相关代码。
package com.wangsaichao.cas.dao.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* @author: wangsaichao
* @date: 2018/8/9
* @description: cas-rest返回cas服务端信息
*/
public class SysUser {
@JsonProperty("id")
@NotNull
private String username;
/**
* 需要返回实现org.apereo.cas.authentication.principal.Principal的类名接口
*/
@JsonProperty("@class")
private String clazz = "org.apereo.cas.authentication.principal.SimplePrincipal";
@JsonProperty("attributes")
private Map attributes = new HashMap<>();
@JsonIgnore
@NotNull
private String password;
/**
* 用户状态,根据状态判断是否可用
*/
@JsonIgnore
private String state;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
public Map getAttributes() {
return attributes;
}
public void setAttributes(Map attributes) {
this.attributes = attributes;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
@JsonIgnore
public SysUser addAttribute(String key, Object val) {
getAttributes().put(key, val);
return this;
}
}
package com.wangsaichao.cas.controller;
import com.wangsaichao.cas.dao.entity.SysUser;
import com.wangsaichao.cas.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.UnsupportedEncodingException;
/**
* @author: wangsaichao
* @date: 2018/8/1
* @description: 对比用户信息
*/
@RestController
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@Autowired
private UserService userService;
/**
* 1. cas 服务端会通过post请求,并且把用户信息以"用户名:密码"进行Base64编码放在authorization请求头中
* 2. 返回200状态码并且格式为{"@class":"org.apereo.cas.authentication.principal.SimplePrincipal","id":"casuser","attributes":{}} 是成功的
* 2. 返回状态码403用户不可用;404账号不存在;423账户被锁定;428过期;其他登录失败
* @param httpHeaders
* @return
*/
@RequestMapping("/login")
public Object login(@RequestHeader HttpHeaders httpHeaders){
logger.info("开始验证服务");
SysUser user = null;
try {
UserTemp userTemp = obtainUserFormHeader(httpHeaders);
//尝试查找用户库是否存在
user = userService.selectUser(userTemp.username);
if (user != null) {
if (!user.getPassword().equals(userTemp.password)) {
//密码不匹配
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
if (!"0".equals(user.getState())) {
//用户已锁定
return new ResponseEntity(HttpStatus.LOCKED);
}
} else {
//不存在 404
return new ResponseEntity(HttpStatus.NOT_FOUND);
}
} catch (UnsupportedEncodingException e) {
logger.error("用户认证错误", e);
new ResponseEntity(HttpStatus.BAD_REQUEST);
}
//成功返回json
return user;
}
/**
* This allows the CAS server to reach to a remote REST endpoint via a POST for verification of credentials.
* Credentials are passed via an Authorization header whose value is Basic XYZ where XYZ is a Base64 encoded version of the credentials.
* @param httpHeaders
* @return
* @throws UnsupportedEncodingException
*/
private UserTemp obtainUserFormHeader(HttpHeaders httpHeaders) throws UnsupportedEncodingException {
//cas服务端会通过把用户信息放在请求头authorization中,并且通过Basic认证方式加密
String authorization = httpHeaders.getFirst("authorization");
if(StringUtils.isEmpty(authorization)){
return null;
}
String baseCredentials = authorization.split(" ")[1];
//用户名:密码
String usernamePassword = new String(Base64Utils.decodeFromString(baseCredentials), "UTF-8");
String[] credentials = usernamePassword.split(":");
return new UserTemp(credentials[0], credentials[1]);
}
/**
* 从请求头中获取用户名和密码
*/
private class UserTemp {
private String username;
private String password;
public UserTemp(String username, String password) {
this.username = username;
this.password = password;
}
}
}
先使用admin登录,之后状态改为锁定,再次登录。