CAS单点登录-自定义认证之Shiro、Rest(六)

来源:互联网 发布:大数据促进旅游产业 编辑:程序博客网 时间:2024/06/06 13:03

CAS单点登录-自定义认证之Shiro、Rest(六)

注意:单点登录版本为cas-5.1.3
若需要上个版本代码,可以点击下载:GitHub 码云

这章即将讲解cas服务端集成shiro认证、Rest认证

上一章讲了自定认证之jdbc 已经解决了很多需求上的问题,但这远远不够,因为需求总是变态的,所以我们要时刻准备着

犹如:

  • 公司老程序员多,用目前使用成熟的技术吧
  • 公司数据安全性有要求,cas不允许连到我的账号库,哦,给我写个接口总可以吧

Shiro认证

接下来不得不分析一下shiro的需求量:

  • 框架使用shiro鉴权 ★★★★☆
  • 熟悉shiro的比较多 ★★★★★
  • 相对于来说轻量级 ★★☆☆☆

当然了使用shiro的公司还是非常的多,听说包括spring的官网也是用shiro的,所以在cas中集成shiro进行鉴权,把老系统的配置直接拿来用,那再好不过了。

pom.xml

添加maven依赖

<dependency>  <groupId>org.apereo.cas</groupId>  <artifactId>cas-server-support-generic</artifactId>  <version>${cas.version}</version></dependency>

加了依赖即将支持三种校验方式,包括文件存储用户校验器、拒绝用户校验器、shiro校验器

这里写图片描述

若对Whitelist(文件校验白名单)、Blacklist(黑名单)机制、配置了解或者有需求疑问感兴趣可以联系博主,这些将不讲解,但附上配置图

这里写图片描述

cas系统配置

# Shiro Authentication 开始#允许登录的用户,必须要有以下权限,否则拒绝,多个逗号隔开cas.authn.shiro.requiredPermissions=staff#允许登录的用户,必须要有以下权限,否则拒绝,多个逗号隔开cas.authn.shiro.requiredRoles=admin#shir配置文件位置cas.authn.shiro.config.location=classpath:shiro.ini#shiro name 唯一cas.authn.shiro.name=cas-shiro# 与Query Authentication一致的加密策略cas.authn.shiro.passwordEncoder.type=DEFAULT# cas.authn.shiro.passwordEncoder.characterEncoding=UTF-8cas.authn.shiro.passwordEncoder.encodingAlgorithm=MD5# Shiro Authentication 结束

shiro.ini:

[main]cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManagersecurityManager.cacheManager = $cacheManager[users]#密码123admin = 202cb962ac59075b964b07152d234b70, admin#不可登录,因为配置了需要角色admin#密码123456test = e10adc3949ba59abbe56e057f20f883e, developer[roles]admin = system,admin,staff,superuser:*developer = commit:*

这个文件就不多说了,主要是看shiro的配置情况,包括可以自定义用户存储策略,校验realm,从侧面来说只需要配置鉴权部分即可

注意: cas-shiro只做Subject.login();另外一个角度说,只做鉴权,不做其他退出之类的,所以在这里filter之类的鉴权器是无作用的跟不用说urls选项的匹配。

测试

尝试登录:

  • admin/123 成功
  • test/123456 失败(没有权限)

Rest 认证

由于架构或公司政策等原因,不得不使用接口来打通数据的问题。

那问题来了:

问:什么是Rest认证?
答:通过数据接口对用户进行认证
问:cas又是怎么做的?
答:通过请求接口,返回固定格式,进行对密码匹配,判断用户是否合法
问:什么场景下用rest认证?用户数据存在远端、不允许cas直接访问帐号数据、cas不希望你知道帐号数据的表结构、存储方式可能目前不满足

配置

application.properties

#REST 认证开始#请求远程调用接口cas.authn.rest.uri=http://localhost:8881/login#加密策略cas.authn.rest.passwordEncoder.type=DEFAULTcas.authn.rest.passwordEncoder.characterEncoding=UTF-8#加密算法cas.authn.rest.passwordEncoder.encodingAlgorithm=MD5#REST 结束

当用户点击登录后,cas会发送post请求到http://localhost:8881/login并且把用户信息以”用户名:密码”进行Base64编码放在authorization请求头中

若输入用户名密码为:admin/123

那么请求头包括:
authorization=Basic Base64(admin+MD5(123))

那么发送后客户端必须响应一下数据,cas明确规定如下:

  1. cas 服务端会通过post请求,并且把用户信息以”用户名:密码”进行Base64编码放在authorization请求头中
  2. 返回200状态码并且格式为{“@class”:”org.apereo.cas.authentication.principal.SimplePrincipal”,”id”:”casuser”,”attributes”:{}} 是成功的; 返回状态码403用户不可用;404账号不存在;423账户被锁定;428过期;其他登录失败

这里写图片描述

客户端校验Demo

SysUser.java

/* * 版权所有.(c)2008-2017. 卡尔科技工作室 */package com.carl.auth.sso.rest.client.bean;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 Carl * @date 2017/9/14 * @since JDK1.7 */public class SysUser {    @JsonProperty("id")    @NotNull    private String username;    @JsonProperty("@class")    //需要返回实现org.apereo.cas.authentication.principal.Principal的类名接口    private String clazz = "org.apereo.cas.authentication.principal.SimplePrincipal";    @JsonProperty("attributes")    private Map<String, Object> attributes = new HashMap<>();    @JsonIgnore    @NotNull    private String password;    @JsonIgnore    //用户是否不可用    private boolean disable = false;    @JsonIgnore    //用户是否过期    private boolean expired = false;    @JsonIgnore    //是否锁定    private boolean locked = false;    public boolean isLocked() {        return locked;    }    public SysUser setLocked(boolean locked) {        this.locked = locked;        return this;    }    public boolean isDisable() {        return disable;    }    public SysUser setDisable(boolean disable) {        this.disable = disable;        return this;    }    public boolean isExpired() {        return expired;    }    public SysUser setExpired(boolean expired) {        this.expired = expired;        return this;    }    public String getPassword() {        return password;    }    public SysUser setPassword(String password) {        this.password = password;        return this;    }    public String getUsername() {        return username;    }    public SysUser setUsername(String username) {        this.username = username;        return this;    }    public String getClazz() {        return clazz;    }    public Map<String, Object> getAttributes() {        return attributes;    }    public SysUser setAttributes(Map<String, Object> attributes) {        this.attributes = attributes;        return this;    }    @JsonIgnore    public SysUser addAttribute(String key, Object val) {        getAttributes().put(key, val);        return this;    }}

而上面的属性,必须跟@class的实现一一对应,如attributes 就是返回属性给对接的客户端,有必要的信息必须返回给cas,cas会进行二次过滤,而二次过滤是属于多属性返回的内容,后面的章节会说明白哦


AuthUserController.java

/* * 版权所有.(c)2008-2017. 卡尔科技工作室 */package com.carl.auth.sso.rest.client.controller;import com.carl.auth.sso.rest.client.bean.SysUser;import com.carl.auth.sso.rest.client.service.UserRepertory;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.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestHeader;import org.springframework.web.bind.annotation.RestController;import java.io.UnsupportedEncodingException;/** * @author Carl * @date 2017/9/14 * @since JDK1.7 */@RestControllerpublic class AuthUserController {    private static final Logger LOGGER = LoggerFactory.getLogger(AuthUserController.class);    @Autowired    private UserRepertory userRepertory;    /**     * 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     */    @PostMapping("/login")    public Object login(@RequestHeader HttpHeaders httpHeaders) {        LOGGER.info("Rest api login.");        LOGGER.debug("request headers: {}", httpHeaders);        SysUser user = null;        try {            UserTemp userTemp = obtainUserFormHeader(httpHeaders);            //尝试查找用户库是否存在            user = userRepertory.getUser(userTemp.username);            if (user != null) {                if (!user.getPassword().equals(userTemp.password)) {                    //密码不匹配                    return new ResponseEntity(HttpStatus.BAD_REQUEST);                }                if (user.isDisable()) {                    //禁用 403                    return new ResponseEntity(HttpStatus.FORBIDDEN);                }                if (user.isLocked()) {                    //锁定 423                    return new ResponseEntity(HttpStatus.LOCKED);                }                if (user.isExpired()) {                    //过期 428                    return new ResponseEntity(HttpStatus.PRECONDITION_REQUIRED);                }            } else {                //不存在 404                return new ResponseEntity(HttpStatus.NOT_FOUND);            }        } catch (UnsupportedEncodingException e) {            LOGGER.error("", e);            new ResponseEntity(HttpStatus.BAD_REQUEST);        }        LOGGER.info("[{}] login is ok", user.getUsername());        //成功返回json        return user;    }    /**     * 根据请求头获取用户名及密码     *     * @param httpHeaders     * @return     * @throws UnsupportedEncodingException     */    private UserTemp obtainUserFormHeader(HttpHeaders httpHeaders) throws UnsupportedEncodingException {        /**         *         * 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.         */        //根据官方文档,当请求过来时,会通过把用户信息放在请求头authorization中,并且通过Basic认证方式加密        String authorization = httpHeaders.getFirst("authorization");//将得到 Basic Base64(用户名:密码)        String baseCredentials = authorization.split(" ")[1];        String usernamePassword = new String(Base64Utils.decodeFromString(baseCredentials), "UTF-8");//用户名:密码        LOGGER.debug("login user: {}", usernamePassword);        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;        }    }}

这段代码核心是实现cas明确要求的返回值,如果敢兴趣,请仔细看注解

若尝试用户rest-locked/admin结果如下
这里写图片描述

总结

  1. shiro的使用率及集成
  2. rest的应用场景及集成

cas的认证方式中,最常见三种校验方式:

  • 数据库查询(上一章)
  • shiro集成验证(本章)
  • 远程rest认证(本章)

相信很多人对自定义校验器非常感兴趣,例如验证码登录、扫码登录等等,那么这些再后面再继续说
因为验证码登录必须先把界面调整,这是主题的范畴,回过头再讲自定义。


下载本章代码:GitHub

注意:

由于版本5.1.3是shiro.ini配置在classpath下会从临时文件中加载,有bug,希望在5.1.4或者5.1.5进行解决,那么如果在5.1.3希望用shiro,配置在其他系统盘绝对路径即可

下载代码后注意查看README.md

模块名 模块介绍 备注 端口情况 必须https path 启动循序 sso-server cas服务 接入鉴权 8443 √ cas 3 sso-config 配置中心 管理各个服务配置 8888 × config 1 sso-rest-client rest验证应用 rest验证应用 8883 × / 2

用户:

用户名 密码 是否可登录 备注 验证器 admin 123 √ 可登录 shiro rest-admin 123 √ 可登录 shiro rest-test 123 √ 可登录 rest rest-locked 123 × 锁定 rest rest-disable 123 × 不可用 rest rest-expired 123 × 过期,修改密码 rest

作者联系方式

如果技术的交流或者疑问可以联系或者提出issue。

邮箱:huang.wenbin@foxmail.com

QQ: 756884434 (请注明:SSO-CSDN)

阅读全文
0 0