前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

接8中代码,使用jdbc md5登录或者使用加密与盐登录。但是下面会介绍自定义验证密码登录,只不过代码不一样。本内容将不会提供代码,因为涉及到隐私信息,如果需要代码,请直接联系博主

1、自定义主题

1、自定义主题的theme默认名字为:apereo

2、service中添加json文件,确定要拦截的url地址,启动主题登录,这里不仅对有证书https的拦截,也对没有证书的http拦截

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "^(https|imaps|http)://.*",
  "name" : "Apereo",
  "theme" : "apereo",
  "id" : 10000002,
  "description" : "Apereo foundation service",
  "evaluationOrder" : 2
}

2、自定义密码校验

针对自定义的盐使用自定义的验证密码工具类,不需要管下面的email,因为从前台传来的username参数会自动进入sql中

1、我自己的MD5验证(与2中无关)


#4.jdbc
#
##Query Database Authentication 数据库查询校验用户名开始
##查询账号密码sql,必须包含密码字段
cas.authn.jdbc.query[0].sql=select * from sys_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
#数据库方言hibernate的知识
cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQL5Dialect
#数据库驱动
cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
#数据库连接
cas.authn.jdbc.query[0].url=jdbc:mysql://localhost:3306/cas?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=true
#数据库用户名
cas.authn.jdbc.query[0].user=root
#数据库密码
cas.authn.jdbc.query[0].password=123456
#默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
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
#Query Database Authentication 数据库查询校验用户名结束



2、自定义密码校验

#数据库连接
cas.authn.jdbc.query[0].url=jdbc:mysql://localhost:3306/cas?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=true
#数据库用户名
cas.authn.jdbc.query[0].user=root
#数据库密码
cas.authn.jdbc.query[0].password=123456
cas.authn.jdbc.query[0].sql=select password from sys_admin_user where email=? and status=1
cas.authn.jdbc.query[0].fieldPassword=password
# 这里就是最重要的一个,用来验证密码的
cas.authn.jdbc.query[0].passwordEncoder.type=com.duodian.admore.utils.CustomPasswordEncoder
cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect
cas.authn.jdbc.query[0].ddlAuto=none
cas.authn.jdbc.query[0].driverClass=com.mysql.cj.jdbc.Driver
cas.authn.jdbc.query[0].leakThreshold=10
cas.authn.jdbc.query[0].propagationBehaviorName=PROPAGATION_REQUIRED
cas.authn.jdbc.query[0].batchSize=1
cas.authn.jdbc.query[0].healthQuery=SELECT 1
cas.authn.jdbc.query[0].failFast=true

public class CustomPasswordEncoder implements PasswordEncoder {

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

	//前台传来的明文密码会进入这里。然后取得加密密码
    @Override
    public String encode(CharSequence password) {
        if (password == null) {
            return null;
        } else {
            return this.encodePassword(password.toString());
        }
    }
	//密码校验
    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        String encodedRawPassword = StringUtils.isNotBlank(rawPassword)?this.encode(rawPassword.toString()):null;
        return StringUtils.equals(encodedRawPassword, encodedPassword);
    }


    private String encodePassword(String password) {
    		//…………………………省略
        //这部分则为通过前台传来的明文密码根据自己的东西返回获取数据库中的加密密码的格式,用于matchs方法中进行比较
        }


3、配置客户端

1、pom.xml

<?xml version="1.0" encoding="UTF-8"?>
	<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
			 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
		<modelVersion>4.0.0</modelVersion>


		<groupId>com.cas.healerjean.client.id</groupId>
		<artifactId>com-cas-healerjean-client</artifactId>
		<version>0.0.1-SNAPSHOT</version>
		<packaging>jar</packaging>

		<name>com-cas-healerjean-client</name>
		<description>Demo project for Spring Boot</description>


		<parent>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-parent</artifactId>
			<version>1.4.4.RELEASE</version>
			<relativePath/> <!-- lookup parent from repository -->
		</parent>

		<properties>
			<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
			<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
			<java.version>1.8</java.version>
			<spring-cloud.version>Edgware.SR2</spring-cloud.version>
		</properties>

		<dependencies>

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>

			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-test</artifactId>
				<scope>test</scope>
			</dependency>

			<dependency>
				<groupId>org.jasig.cas.client</groupId>
				<artifactId>cas-client-core</artifactId>
				<version>3.4.1</version>
			</dependency>

			<dependency>
				<groupId>org.springframework.security</groupId>
				<artifactId>spring-security-core</artifactId>
				<version>4.2.3.RELEASE</version>
			</dependency>

			<dependency>
				<groupId>org.springframework.security</groupId>
				<artifactId>spring-security-web</artifactId>
				<version>4.2.3.RELEASE</version>
			</dependency>

		</dependencies>


		<build>
			<plugins>
				<plugin>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-maven-plugin</artifactId>
				</plugin>
			</plugins>
		</build>


	</project>


2、配置文件

server.port=8083

#cas
cas.server.url.prefix=http://localhost:8443/cas
cas.server.url.login=${cas.server.url.prefix}/login
cas.client.name=http://localhost:${server.port}

3、配置单点登录

package com.cas.healerjean.client.config;

import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.jasig.cas.client.util.AssertionHolder;
import org.jasig.cas.client.util.AssertionThreadLocalFilter;
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;

/**
 * CasFilterConfig
 *
 * @author chuan.ma
 * @since 2017/6/27
 */
@Configuration
public class CasConfig {

    @Value("${cas.server.url.login}")
    public String casServerLoginUrl;

    @Value("${cas.server.url.prefix}")
    public String casServerUrlPrefix;

    @Value("${cas.client.name}")
    public String casClientName;

    /**
     * 用于实现单点登出功能
     */
    @Bean
    public ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> singleSignOutHttpSessionListener() {
        ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> listener = new ServletListenerRegistrationBean<>();
        listener.setEnabled(true);
        listener.setListener(new SingleSignOutHttpSessionListener());
        listener.setOrder(1);
        return listener;
    }


    /**
     * 该过滤器用于实现单点登出功能,单点退出配置,一定要放在其他filter之前
     */
    @Bean
    public FilterRegistrationBean logOutFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();

        LogoutFilter logoutFilter = new LogoutFilter(casServerUrlPrefix + "/logout?service=" + casClientName,new SecurityContextLogoutHandler());
        filterRegistration.setFilter(logoutFilter);
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/logout");
        filterRegistration.addInitParameter("casServerUrlPrefix", casServerUrlPrefix);
        filterRegistration.addInitParameter("serverName", casClientName);
        filterRegistration.addInitParameter("redirectAfterValidation", "true");
        filterRegistration.setOrder(2);
        return filterRegistration;
    }

    /**
     * CAS Single Sign Out Filter
     * 该过滤器用于实现单点登出功能,单点退出配置,一定要放在其他filter之前
     */
    @Bean
    public FilterRegistrationBean singleSignOutFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new SingleSignOutFilter());
        filterRegistration.addUrlPatterns("/*");
        filterRegistration.addInitParameter("casServerUrlPrefix", casServerUrlPrefix);
        filterRegistration.addInitParameter("serverName", casClientName);
        filterRegistration.setOrder(3);
        return filterRegistration;
    }

    /**
     * 该过滤器负责用户的认证工作
     */
    @Bean
    public FilterRegistrationBean authenticationFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new AuthenticationFilter());
        filterRegistration.addUrlPatterns("/*");
        filterRegistration.addInitParameter("ignorePattern","/error|/public*|/assets*|/ftl*");
        filterRegistration.addInitParameter("casServerLoginUrl", casServerLoginUrl);
        filterRegistration.addInitParameter("encoding","UTF-8");
        filterRegistration.addInitParameter("serverName", casClientName);
        filterRegistration.addInitParameter("useSession", "true");
        filterRegistration.setOrder(4);
        return filterRegistration;
    }


    /**
     * 该过滤器负责对Ticket的校验工作
     */
    @Bean
    public FilterRegistrationBean cas20ProxyReceivingTicketValidationFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        Cas30ProxyReceivingTicketValidationFilter filter = new Cas30ProxyReceivingTicketValidationFilter();
        filterRegistration.setFilter(filter);
        filterRegistration.addUrlPatterns("/*");
        filterRegistration.addInitParameter("encoding","UTF-8");
        filterRegistration.addInitParameter("casServerUrlPrefix", casServerUrlPrefix);
        filterRegistration.addInitParameter("serverName", casClientName);
        filterRegistration.setOrder(5);
        return filterRegistration;
    }

    /**
     * 该过滤器对HttpServletRequest请求包装, 可通过HttpServletRequest的getRemoteUser()方法获得登录用户的登录名
     */
    @Bean
    public FilterRegistrationBean httpServletRequestWrapperFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new HttpServletRequestWrapperFilter());
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/*");
        filterRegistration.setOrder(6);
        return filterRegistration;
    }

    /**
     * 该过滤器使得可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。
     * 比如AssertionHolder.getAssertion().getPrincipal().getName()。
     * 这个类把Assertion信息放在ThreadLocal变量中,这样应用程序不在web层也能够获取到当前登录信息
     */
    @Bean
    public FilterRegistrationBean assertionThreadLocalFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new AssertionThreadLocalFilter());
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/*");
        filterRegistration.setOrder(7);
        return filterRegistration;
    }


    public static class RemoteUserUtil {

        public static Boolean hasLogin(){
            return AssertionHolder.getAssertion() != null;
        }

        /**
         * 获取单点登录用户id
         * @return
         */
        public static Long getRemoteUserId(){
            Object userId = AssertionHolder.getAssertion().getPrincipal().getAttributes().get("id");
            return userId == null ? null : Long.parseLong(userId.toString());
        }

        /**
         * 获取单点登录用户账户
         * @return
         */
        public static String getRemoteUserAccount(){
            return AssertionHolder.getAssertion().getPrincipal().getName();
        }

        /**
         * 获取单点登录用户名称
         * @return
         */
        public static String getRemoteUserName(){
            Object userName = AssertionHolder.getAssertion().getPrincipal().getAttributes().get("name");
            return userName == null ? null : userName.toString();
        }

       
    }
}


4、客户端获取从CAS服务端过来的用户名


@RestController
public class HomeController {

    Logger logger = LoggerFactory.getLogger(HomeController.class);
    @GetMapping("hello")
    public String home(HttpServletRequest request){



        String remoteUser =request.getRemoteUser();
        logger.info("1、request.getRemoteUser()"+remoteUser);



        //断言
        Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
        AttributePrincipal principal = assertion.getPrincipal();
        String username =  principal.getName();
        logger.info("2、AttributePrincipal.getName"+username);


         String user3 = AssertionHolder.getAssertion().getPrincipal().getName();
        logger.info("3、AssertionHolder.getAssertion().getPrincipal().getName()"+user3);

        return  null;
    }
}


启动服务端8443和客户端8083

浏览器访问:http://localhost:8083/hello ,自动会进入登录页面http://localhost:8443/cas/login?service=http%3A%2F%2Flocalhost%3A8083%2Fhello

通过用户名密码 zhangsan 12345678,登录成功,观察客户端后台logger打印内容 WX20180309-140403

5、服务端返回更多的用户信息,下面的service json其实之前就配置了。知识为了客户端能够向服务端发起登录请求。与返回更多信息没关系

1、service中添加json

HTTPSandIMAPS-10000001.json

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "^(https|imaps|http)://.*",
  "name" : "HTTPS and IMAPS",
  "id" : 10000001,
  "description" : "This service definition authorizes all application urls that support HTTPS and IMAPS and HTTP protocols.",
  "evaluationOrder" : 1,
  "attributeReleasePolicy": {
    "@class": "org.apereo.cas.services.ReturnAllAttributeReleasePolicy"
  }
}

2、properties中配置查询返回配置


cas.authn.attributeRepository.jdbc[0].singleRow=true
cas.authn.attributeRepository.jdbc[0].order=0
cas.authn.attributeRepository.jdbc[0].url=jdbc:mysql://localhost:3306/cas?allowMultiQueries=true&zeroDateTimeBehavior=convertToNull
cas.authn.attributeRepository.jdbc[0].user=root
cas.authn.attributeRepository.jdbc[0].password=123456

#username 返回登录的时候的提供的用户名可以配置为和数据库一致,观察ddkj即可发现这里配置的为email
cas.authn.attributeRepository.jdbc[0].username=username
cas.authn.attributeRepository.jdbc[0].sql=select * from sys_user where username=?
# 下面这里为配置要返回的信息,要返回的信息为email,address
cas.authn.attributeRepository.jdbc[0].attributes.email=email
cas.authn.attributeRepository.jdbc[0].attributes.address=address
cas.authn.attributeRepository.jdbc[0].dialect=org.hibernate.dialect.MySQLDialect
cas.authn.attributeRepository.jdbc[0].ddlAuto=none
cas.authn.attributeRepository.jdbc[0].driverClass=com.mysql.cj.jdbc.Driver
cas.authn.attributeRepository.jdbc[0].leakThreshold=10
cas.authn.attributeRepository.jdbc[0].propagationBehaviorName=PROPAGATION_REQUIRED
cas.authn.attributeRepository.jdbc[0].batchSize=1
cas.authn.attributeRepository.jdbc[0].healthQuery=SELECT 1
cas.authn.attributeRepository.jdbc[0].failFast=true


3、客户端接收从服务端返回来的更新信息

package com.cas.healerjean.client.controller;

import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.AssertionHolder;
import org.jasig.cas.client.validation.Assertion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@RestController
public class HomeController {

    Logger logger = LoggerFactory.getLogger(HomeController.class);
    @GetMapping("hello")
    public String home(HttpServletRequest request){



        String remoteUser =request.getRemoteUser();
        logger.info("1、request.getRemoteUser()"+remoteUser);



        //断言
        Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
        AttributePrincipal principal = assertion.getPrincipal();
        String username =  principal.getName();
        logger.info("2、AttributePrincipal.getName"+username);


         String user3 = AssertionHolder.getAssertion().getPrincipal().getName();
        logger.info("3、AssertionHolder.getAssertion().getPrincipal().getName()"+user3);

        /**
         * 加上 其他参数以后的
         */
        String email =   AssertionHolder.getAssertion().getPrincipal().getAttributes().get("email").toString();
        String address =   AssertionHolder.getAssertion().getPrincipal().getAttributes().get("address").toString();
        logger.info("4、邮箱和地址:"+email+address);
        return  null;
    }
}


WX20180309-141425

本次涉及隐私,不提供服务端代码,只提供客户端代码,如果需要代码,请和博主联系。谢谢

ContactAuthor