CAS 5.3自定义 登录

自定义认证校验策略
我们知道CAS为我们提供了多种认证数据源,我们可以选择JDBC、File、JSON等多种方式,但是如果我想在自己的认证方式中可以根据提交的信息实现不同数据源选择,这种方式就需要我们去实现自定义认证。

自定义策略主要通过现实更改CAS配置,通过AuthenticationHandler在CAS中设计和注册自定义身份验证策略,拦截数据源达到目的。

主要分为下面三个步骤:

设计自己的认证处理数据的程序
注册认证拦截器到CAS的认证引擎中
更改认证配置到CAS中
首先我们还是添加需要的依赖库:

   <!-- Custom Authentication -->
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-core-authentication-api</artifactId>
        <version>${cas.version}</version>
    </dependency>

    <!-- Custom Configuration -->
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-core-configuration-api</artifactId>
        <version>${cas.version}</version>
    </dependency>

    <!-- SpringSecurity Core -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-core</artifactId>
        <version>5.0.8.RELEASE</version>
    </dependency>

如果我们认证的方式仅仅是传统的用户名和密码,实现AbstractUsernamePasswordAuthenticationHandler这个抽象类就可以了,官方
给的实例也是这个。

在编写自定义认证类之前,我们这里说明下为什么选择使用springSecurity提供的加解密工具类:BCryptPasswordEncoder

BCryptPasswordEncoder相关知识:
用户表的密码通常使用MD5等不可逆算法加密后存储,为防止彩虹表破解更会先使用一个特定的字符串(如域名)加密,然后再使用一个随机的salt(盐值)加密。
特定字符串是程序代码中固定的,salt是每个密码单独随机,一般给用户表加一个字段单独存储,比较麻烦。
BCrypt算法将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理salt问题。
补充说明:即使不同的用户注册时输入相同的密码,存入数据库的密文密码也会不同。

官方的实例有一个坑,给出的是5.2.x版本以前的例子,5.3.x版本后的jar包更改了,而且有个地方有坑,在5.2.x版本前的可以,新的5.3.x是不行的。

注意:这里试了很多版本最后 5.3.9这个版本没问题

接着我们自定义我们自己的实现类CustomUsernamePasswordAuthentication,如下:

package com.thtf.cas.config;

import com.thtf.cas.model.User;
import com.thtf.cas.util.SpringSecurityUtil;
import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult;
import org.apereo.cas.authentication.MessageDescriptor;
import org.apereo.cas.authentication.PreventedException;
import org.apereo.cas.authentication.UsernamePasswordCredential;
import org.apereo.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.security.auth.login.AccountException;
import javax.security.auth.login.FailedLoginException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**

  • ========================

  • 自定义认证校验策略

  • Created with IntelliJ IDEA.

  • User:pyy

  • Date:2019/6/26

  • Time:16:05

  • Version: v1.0

  • ========================
    */
    public class CustomUsernamePasswordAuthentication extends AbstractUsernamePasswordAuthenticationHandler {

    private final static Logger logger = LoggerFactory.getLogger(CustomUsernamePasswordAuthentication.class);

    private JdbcTemplate jdbcTemplate;

    public CustomUsernamePasswordAuthentication(String name, ServicesManager servicesManager,
    PrincipalFactory principalFactory, Integer order, JdbcTemplate jdbcTemplate) {
    super(name, servicesManager, principalFactory, order);
    this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(
    UsernamePasswordCredential credential, String originalPassword)
    throws GeneralSecurityException, PreventedException {
    String username = credential.getUsername();
    String password = credential.getPassword();
    logger.info(“认证用户 username = {}”, username);

     String sql = "select id, username, password, expired, disable, email FROM sys_user where username = ?";
     User info = (User) jdbcTemplate.queryForObject(sql, new Object[]{username}, new BeanPropertyRowMapper(User.class));
     if (info == null) {
         logger.info("用户不存在!");
         throw new AccountException("用户不存在!");
     }
    
     if (!SpringSecurityUtil.checkpassword(password, info.getPassword())) {
         logger.info("密码错误!");
         throw new FailedLoginException("密码错误!");
     } else {
         logger.info("校验成功");
         //可自定义返回给客户端的多个属性信息
         HashMap<String, Object> returnInfo = new HashMap<>();
         returnInfo.put("id", info.getId());
         returnInfo.put("username", info.getUsername());
         returnInfo.put("expired", info.getExpired());
         returnInfo.put("disable", info.getDisable());
         returnInfo.put("email", info.getEmail());
         final List<MessageDescriptor> list = new ArrayList<>();
         return createHandlerResult(credential, this.principalFactory.createPrincipal(username, returnInfo), list);
     }
    

    }
    }

这里给出的与官方实例不同在两个地方:

返回的为 AuthenticationHandlerExecutionResult而不是HandlerResult,其实源码是一样的,在新版本重新命名了而已。
createHandlerResult传入的warings不能为null,不然程序运行后提交信息始终无法认证成功!!!
代码主要通过拦截传入的Credential,获取用户名和密码,然后再自定义返回给客户端的用户信息。这里便可以通过代码方式自定义返回给客户端多个不同属性信息。

注入配置信息
继承AuthenticationEventExecutionPlanConfigurer

package com.thtf.cas.config;

import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
import org.apereo.cas.authentication.AuthenticationHandler;
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.services.ServicesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

@Configuration(“CustomAuthenticationConfiguration”)
@EnableConfigurationProperties({CasConfigurationProperties.class, DataBaseProperties.class})
public class CustomAuthenticationConfiguration implements AuthenticationEventExecutionPlanConfigurer {

@Autowired
private DataBaseProperties databaseProperties;

@Autowired
private JdbcTemplate jdbcTemplate;

@Autowired
private CasConfigurationProperties casProperties;

@Autowired
@Qualifier("servicesManager")
private ServicesManager servicesManager;

@Bean 
public AuthenticationHandler myAuthenticationHandler() { 
    return new CustomUsernamePasswordAuthentication(CustomUsernamePasswordAuthentication.class.getName(), servicesManager, new DefaultPrincipalFactory(), 1, jdbcTemplate); 
}

@Override
public void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) {
    plan.registerAuthenticationHandler(myAuthenticationHandler());
}

@Bean
public JdbcTemplate jdbcTemplate(){
    // JDBC模板依赖于连接池来获得数据的连接,所以必须先要构造连接池 
    DriverManagerDataSource dataSource = new DriverManagerDataSource(); 
    dataSource.setDriverClassName(databaseProperties.getDriverClass()); 
    dataSource.setUrl(databaseProperties.getUrl()); 
    dataSource.setUsername(databaseProperties.getUser()); 
    dataSource.setPassword(databaseProperties.getPassword()); 
    // 创建JDBC模板 
    JdbcTemplate jdbcTemplate = new JdbcTemplate(); 
    jdbcTemplate.setDataSource(dataSource); 
    return jdbcTemplate;
}

}

这里涉及到JdbcTemplate数据源配置如下:
数据源配置:

########## 用户认证JDBC数据源配置 ############
sso.jdbc.user=root
sso.jdbc.password=123456
sso.jdbc.driverClass=com.mysql.jdbc.Driver
sso.jdbc.url=jdbc:mysql://localhost:3306/sso?characterEncoding=utf8
DataBaseProperties:

package com.thtf.cas.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.Serializable;

@Component
@ConfigurationProperties(prefix = “sso.jdbc”)
@Data
public class DataBaseProperties implements Serializable {

private String user;

private String password;

private String driverClass;

private String url;

}
最后我们我们在src/main/resources目录下新建META-INF目录,同时在下面新建spring.factories文件,将配置指定为我们自己新建的信息。

启动应用,输入用户名和密码,查看控制台我们打印的信息,可以发现我们从登陆页面提交的数据以及从数据库中查询到的数据,匹配信息,登录认证成功!!

从而现实了我们自定义用户名和密码的校验,同时我们还可以选择不同的数据源方式。

补充
可能还有读者提出疑问,我提交的信息不止用户名和密码,那该如何自定义认证?
这里就要我们继承AbstractPreAndPostProcessingAuthenticationHandler这个借口,其实上面的AbstractUsernamePasswordAuthenticationHandler就是继承实现的这个类,它只是用于简单的用户名和密码的校验。我们可以查看源码,如下:

参考:https://www.jianshu.com/p/54646ac96473

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Cas 5.3 pac4j是一个用于身份验证和授权的Java库。它提供了一种简单且可扩展的方式来集成CAS(Central Authentication Service)协议和pac4j库,用于集中身份验证。CAS是一种单点登录协议,它允许用户在一次登录后访问多个应用程序而无需再次登录。pac4j是一个Java库,用于实现身份验证和授权的安全框架。 使用Cas 5.3 pac4j,开发人员可以轻松地实现CAS协议和pac4j库的集成,并集中管理用户的身份验证和授权。它提供了多种身份验证方法,包括用户名密码,OAuth,OpenID等。开发人员只需配置相应的身份验证器即可使用这些方法。 该库还提供了丰富的授权机制,允许开发人员定义访问控制规则,以决定哪些用户可以访问特定资源。这些规则可以基于用户角色,用户组,IP地址等进行配置,并通过简单的配置文件进行管理。 Cas 5.3 pac4j还支持自定义的身份验证器和授权器,使开发人员可以根据自己的需要进行扩展和定制。此外,它还提供了易于使用的API,方便开发人员在应用程序中使用身份验证和授权功能。 总之,Cas 5.3 pac4j提供了一个强大而灵活的身份验证和授权解决方案,帮助开发人员快速集成CAS协议和pac4j库,并集中管理用户的身份验证和授权。 ### 回答2: Cas 5.3是一个开源的单点登录(SSO)协议,用于统一认证和授权系统。Pac4j是一个在Cas 5.3上构建的Java安全库。 Cas是“Central Authentication Service”的缩写,主要用于企业或组织中的应用程序和服务之间的身份验证问题。Cas 5.3是Cas协议的一个版本,它提供了多种身份验证方式,包括用户名/密码、第三方账号、Token和验证码等。它的核心原理是通过一个中央认证服务器来验证用户的身份,并将认证结果传递给各个应用程序,实现了用户在一个应用中登录后,其身份在其他应用中的自动认证。 Pac4j是一个开源的Java安全库,它在Cas 5.3上提供了更强大和灵活的安全功能。Pac4j可以集成多种身份验证方式,包括Cas、OAuth、SAML、OpenID Connect、LDAP和JWT等,并能够与各种框架(如Spring、Play、Vert.x)无缝集成。Pac4j还支持身份授权和权限管理,可以根据用户的角色和权限对资源进行访问控制。 Cas 5.3和Pac4j的结合可以实现一个健全的身份认证和授权体系。它们可以应用于各种场景,如企业内部应用的统一登录、多租户系统的身份管理和第三方应用的认证授权等。通过使用Cas 5.3和Pac4j,可以大大简化安全开发的复杂性,提高系统的安全性和用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值