zoukankan      html  css  js  c++  java
  • CAS单点登录(三)——多种认证方式

    CAS单点登录(三)——多种认证方式

    https://blog.csdn.net/Anumbrella/article/details/81149249

    今天我们讲解一下CAS的多种认证方式,在上一篇文章中我们讲解了CAS基础服务的搭建,完成了CAS认证服务中心的简单部署,如果忘记了话,可以先去复习一下——CAS单点登录(二)——搭建基础服务。

    CAS认证方式有多种,我们可以根据自己的需求来实现。在前一篇文章中搭建服务中我们其实就把CAS的JDBC认证方式讲解了,在读取用户名我们可以指定为从数据库的表中读取数据。

    一、JDBC认证
    我们还是接着上次的代码进行讲解,你可以先下载先前的代码——Chapter1。上次在CAS基本服务搭建的代码中,我们只是简单的使用了一下,今天我们将完善更多的配置。

    在前面我们设计了一个user表,表的字段为:

    字段名 字段类型 备注
    id bigint ID
    username varchar 用户名
    password varchar 用户密码
    expired bigint 过期字段,1为过期,需修改密码
    disabled bigint 不可用字段,1为不可用,禁用
    常用单向加密算法:MD5、SHA、HMAC。

    一般我们常用的加密算法就这几种。在JDBC认证中我们也可以选择配置加密算法,加密算法一般为上面的三种,MD5、SHA、HMAC,加密类型为NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2这几种,我们在配置文件中选择加密类型,指定加密算法。

    前面配置不变指定JDBC配置,后面的配置为密码加密策略,配置如下:

    ##
    # JDBC配置
    #
    #查询账号密码SQL,必须包含密码字段
    cas.authn.jdbc.query[0].sql=select * from user where username=?

    #指定上面的SQL查询字段名(必须)
    cas.authn.jdbc.query[0].fieldPassword=password

    #指定过期字段,1为过期,若过期不可用
    cas.authn.jdbc.query[0].fieldExpired=expired

    #为不可用字段段,1为不可用,需要修改密码
    cas.authn.jdbc.query[0].fieldDisabled=disabled

    #数据库连接
    cas.authn.jdbc.query[0].url=jdbc:mysql://127.0.0.1:3306/cas?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false

    #数据库dialect配置
    cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect

    #数据库用户名
    cas.authn.jdbc.query[0].user=root

    #数据库用户密码
    cas.authn.jdbc.query[0].password=123

    #数据库事务自动提交
    cas.authn.jdbc.query[0].autocommit=false

    #数据库驱动
    cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver

    #超时配置
    cas.authn.jdbc.query[0].idleTimeout=5000

    #默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
    # NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
    cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT
    # 字符类型
    cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
    # 加密算法
    cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5
    # 加密盐
    #cas.authn.jdbc.query[0].passwordEncoder.secret=
    # 加密字符长度
    #cas.authn.jdbc.query[0].passwordEncoder.strength=16
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    然后我们启动应用。当我们运行起CAS,输入原来的用户名和密码——anumbrella/anumbrella,并不能登录,因为我们更改密码验证为MD5加密模式了。将anumbrella使用md5加密后,填入数据库,再次登录,可以发现登录成功。由此验证我们加密成功!

    接着我们再新增用户test、test2、test3,密码分别为用户名md5加密,而test2的expired,test3的disabled都为1。如下:


    因此当我们登录test2和test3用户时,将会有需要更改密码和禁用提示。

    除此之外如果我们要自定义加密类型,就需要实现org.springframework.security.crypto.password.PasswordEncoder接口,并且把类名配置在passwordEncoder.type。

    这里配置为自定义加密,新建类MyPasswordEncoder。

    package net.anumbrella.sso;

    import org.springframework.security.crypto.password.PasswordEncoder;

    /**
    * @author anumbrella
    */
    public class MyPasswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence charSequence) {
    // charSequence为输入的用户密码
    return charSequence.toString();
    }

    @Override
    public boolean matches(CharSequence charSequence, String str) {
    // 当encode方法返回不为null时,matches方法才会调用,charSequence为encode返回的字符串
    // str字符串为数据库中密码字段返回的值
    String encodeStr = charSequence.toString() + "aa";
    if (encodeStr.equals(str)) {
    return true;
    }
    return false;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    更改配置为:

    cas.authn.jdbc.query[0].passwordEncoder.type=net.anumbrella.sso.MyPasswordEncoder
    1
    更改anumbrella用户密码为11aa,启动应用,根据加密算法只要我们登录输入密码11即可验证成功。

    如果要密码无加密,调整passwordEncoder.type=NONE。
    如果要加密策略为SHA,调整passwordEncoder.encodingAlgorithm=SHA。

    到此JDBC的认证方式我们就讲解完毕了,关于更多的使用参考文档:

    密码加密配置:

    https://apereo.github.io/cas/5.3.x/installation/Configuration-Properties-Common.html#password-encoding

    二、白名单(Whitelist)认证
    CAS同时也支持白名单认证方式,主要是File和JSON格式。

    File形式:

    添加依赖包:

    <dependency>
    <groupId>org.apereo.cas</groupId>
    <artifactId>cas-server-support-generic</artifactId>
    <version>${cas.version}</version>
    </dependency>
    1
    2
    3
    4
    5
    6
    在配置文件中添加如下配置:

    ##
    # 白名单——file配置
    #
    cas.authn.file.separator=::
    cas.authn.file.filename=file:///Users/anumbrella/file
    cas.authn.file.name=
    1
    2
    3
    4
    5
    6
    在/Users/anumbrella目录下,新建file文件,内容如下:

    anumbrella::anumbrella
    test::test
    test2::test2
    1
    2
    3
    该文件配置对应的就是用户名和密码,重启CAS,可以发现配置生效了。

    同样的如果我们要配置密码加密,与上面JDBC配置加密一样,更改配置文件如下:

    #默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
    # NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
    cas.authn.file.passwordEncoder.type=DEFAULT
    # 字符类型
    cas.authn.file.passwordEncoder.characterEncoding=UTF-8
    # 加密算法
    cas.authn.file.passwordEncoder.encodingAlgorithm=MD5
    1
    2
    3
    4
    5
    6
    7
    其他都完全一致,只是更改配置文件前部分更改为对应的认证类型的即可。

    JSON形式:

    与File相似,一样添加依赖包:

    <dependency>
    <groupId>org.apereo.cas</groupId>
    <artifactId>cas-server-support-generic</artifactId>
    <version>${cas.version}</version>
    </dependency>
    1
    2
    3
    4
    5
    在配置文件中添加如下配置:

    ##
    # 白名单——json配置
    #
    cas.authn.json.location=file:///Users/anumbrella/file.json
    cas.authn.json.name=
    1
    2
    3
    4
    5
    在/Users/anumbrella目录下,新建file.json文件,内容如下:

    {
    "@class" : "java.util.LinkedHashMap",
    "anumbrella" : {
    "@class" : "org.apereo.cas.adaptors.generic.CasUserAccount",
    "password" : "anumbrella",
    "attributes" : {
    "@class" : "java.util.LinkedHashMap",
    "firstName" : "shu",
    "lastName" : "yun"
    },
    "status" : "OK",
    "expirationDate" : "2018-10-19"
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    用户名和密码仍然为anumbrella/anumbrella。

    同样的如果我们要配置密码加密,更改配置文件如下:

    #默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
    # NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
    cas.authn.json.passwordEncoder.type=DEFAULT
    # 字符类型
    cas.authn.json.passwordEncoder.characterEncoding=UTF-8
    # 加密算法
    cas.authn.json.passwordEncoder.encodingAlgorithm=MD5
    1
    2
    3
    4
    5
    6
    7
    三、黑名单(Blacklist)认证
    在CAS中黑名单的配置就比较简单,配置如下:

    ##
    # 黑名单配置
    #
    cas.authn.reject.users=test,anumbrella
    cas.authn.reject.name=
    1
    2
    3
    4
    5
    当不在黑名单中的用户,就会全部接受,用户甚至乱输入密码都可实现登录。

    同样的如果我们要配置密码加密,更改配置文件如下:

    #默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
    # NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
    cas.authn.reject.passwordEncoder.type=DEFAULT
    # 字符类型
    cas.authn.reject.passwordEncoder.characterEncoding=UTF-8
    # 加密算法
    cas.authn.reject.passwordEncoder.encodingAlgorithm=MD5
    1
    2
    3
    4
    5
    6
    7
    四、Shiro认证
    我们知道Shiro是个一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。通过使用Shiro可以快速搭建一套角色、权限控制的流程,因此Shiro使用的机会还是很大的。

    添加依赖包如下:

    <dependency>
    <groupId>org.apereo.cas</groupId>
    <artifactId>cas-server-support-shiro-authentication</artifactId>
    <version>${cas.version}</version>
    </dependency>
    1
    2
    3
    4
    5
    6
    在配置文件添加如下:

    ##
    # Shiro配置
    #
    #允许登录的用户,必须要有以下权限,否则拒绝,多个逗号隔开
    cas.authn.shiro.requiredPermissions=staff
    #允许登录的用户,必须要有以下角色,否则拒绝,多个逗号隔开
    cas.authn.shiro.requiredRoles=admin
    #shir配置文件位置
    cas.authn.shiro.location=classpath:shiro.ini
    #shiro name 唯一
    cas.authn.shiro.name=cas-shiro
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    在resources下新建shiro.ini文件,配置如下:

    [main]
    cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
    securityManager.cacheManager = $cacheManager

    [users]
    anumbrella = 123, admin
    test = test, developer

    [roles]
    admin = system,admin,staff,superuser:*
    developer = commit:*
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    这里的shiro.ini文件,主要是看shiro的配置情况,可以根据具体的需求进行更改。

    重启CAS服务,根据我们的配置可以发现anumbrella用户可以登录,test用户登录失败,没有相应的权限。

    同样的如果我们要配置密码加密,更改配置文件如下:

    #默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
    # NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
    cas.authn.shiro.passwordEncoder.type=DEFAULT
    # 字符类型
    cas.authn.shiro.passwordEncoder.characterEncoding=UTF-8
    # 加密算法
    cas.authn.shiro.passwordEncoder.encodingAlgorithm=MD5
    1
    2
    3
    4
    5
    6
    7
    五、Rest认证
    这里的Rest认证就是指通过数据接口对用户进行认证,通过发起一个POST请求来进行认证。
    当用户点击登录后,CAS会发送POST请求到在请求头中包含一个Authorization认证,里面的值为Basic XYZ,而这个XYZ就是通过Base64编码后的用户信息。

    比如:
    若输入用户名密码为:anumbrella/123

    那么请求头包括:
    authorization=Basic Base64(anumbrella:123)

    同样的添加依赖包:

    <dependency>
    <groupId>org.apereo.cas</groupId>
    <artifactId>cas-server-support-rest-authentication</artifactId>
    <version>${cas.version}</version>
    </dependency>
    1
    2
    3
    4
    5
    然后再配置文件中添加配置:

    ##
    # Rest配置
    #
    cas.authn.rest.uri=http://localhost:8088/login
    cas.authn.rest.name=
    1
    2
    3
    4
    5
    如果登录成功响应200,返回响应中包含id和attributes字段,如下:

    {"@class":"org.apereo.cas.authentication.principal.SimplePrincipal","id":"casuser","attributes":{}}
    1
    如果失败,返回的结果可能如下:

    返回状态码:403用户不可用;404账号不存在;423账户被锁定;412过期;428密码需要更改;其他登录失败

    我们新建一个Spring Boot服务,用来模拟Rest请求的地址的服务,新建SysUser类,用来定义返回约束的json。

    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<String, Object>();

    @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;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    然后再Controller层里,定义路径/login的POST方法。

    @RestController
    public class RestAuthController {

    private static final Logger LOGGER = LoggerFactory.getLogger(RestAuthController.class);

    /**
    * 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 = new SysUser();
    user.setUsername("anumbrella");
    user.setPassword("123");
    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 = Base64Utils.decoder(baseCredentials);//用户名:密码
    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;
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    我们使用PostMan模拟CAS服务登录,向http://localhost:8088/login发起一个POST请求,请求中包含用户名和密码的认证信息,在Rest服务中,我将用户名和密码写死了,定为anumbrella/123,当然这里可以按具体需求连接数据库来实现。如下:

    最后返回我们期望的结果,现在我们更改CAS配置rest地址为http://localhost:8088/login,重启服务,然后输入密码登录测试。

    可以发现登录成功,我们实现了Rest服务认证。

    同样的,这里我们密码没有进行加密配置。如果我们要配置密码加密,更改配置文件如下:

    #默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
    # NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
    cas.authn.rest.passwordEncoder.type=DEFAULT
    # 字符类型
    cas.authn.rest.passwordEncoder.characterEncoding=UTF-8
    # 加密算法
    cas.authn.rest.passwordEncoder.encodingAlgorithm=MD5
    1
    2
    3
    4
    5
    6
    7
    到这里CAS的多种认证方式就讲完了,当然CAS的认证还有很多种,可以去查看官方的文档——配置文档。

    代码实例:Chapter2

    参考
    https://apereo.github.io/cas/5.3.x/installation/Configuration-Properties.html
    CAS单点登录-自定义认证之Shiro、Rest(六)
    ————————————————
    版权声明:本文为CSDN博主「Anumbrella」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/Anumbrella/article/details/81149249

  • 相关阅读:
    开放的Web平台才是是我们想要的——HTML5变为HTML
    Web字体格式介绍及浏览器兼容性一览
    E百科 | 基于MEC的边缘AI服务
    技术改变生活 浅谈阿里云混合云的探索与实践
    阿里云CDN产品经理陈章炜:边缘创新技术和落地实践
    什么是微内核架构设计?
    技术干货 | 深度解构 Android 应用面临紧急发版时的救星方案:mPaaS 热修复——DexPatch
    如何接地气地接入微前端?
    阿里云原生应用安全防护实践与 OpenKruise 的新领域
    函数计算镜像加速:从分钟到秒的跨越
  • 原文地址:https://www.cnblogs.com/handsome1013/p/12394447.html
Copyright © 2011-2022 走看看