El inicio rápido de la serie de inicio de sesión único de CAS en el tutorial real (4.2.7)

@ @

1. Introducción a SSO

1.1 Definición de inicio de sesión único

Inicio de sesión único (inicio de sesión único), la abreviatura de nombre en inglés SSO, SSO significa que en un entorno multisistema, inicie sesión en un sistema de una sola parte, puede acceder al sistema de confianza relevante sin tener que iniciar sesión nuevamente. En otras palabras, solo necesita iniciar sesión en el sistema individual una vez.

1.2 Rol de inicio de sesión único

El inicio de sesión único generalmente incluye los siguientes tres roles:

① Usuario (múltiple);

② Centro de certificación (uno);

③ Aplicación web (múltiple).

PD: La aplicación web mencionada aquí puede entenderse como Cliente SSO, y se puede decir que el centro de certificación es Servidor SSO.

1.3 Clasificación del inicio de sesión único

Debido a que el protocolo http es un protocolo sin estado, para mantener el estado de inicio de sesión, la información de inicio de sesión debe almacenarse. Según el método de almacenamiento, el método de implementación de inicio de sesión único se puede dividir en dos tipos.

  • Una se basa en cookies, que es más común. Por ejemplo, el CAS descrito a continuación también se basa en cookies;
  • El otro se basa en la sesión. De hecho , se entiende que es el intercambio de sesiones. Solo implementando el intercambio de sesiones entre diferentes subsistemas puede lograr el inicio de sesión único . Para obtener más información, consulte mi blog anterior, que es para lograr el intercambio de sesiones para lograr el inicio de sesión único. Enlace

2. Introducción a CAS

2.1 Definición simple de CAS

CAS (Center Authentication Service) es un proyecto de inicio de sesión único de código abierto estudiado por la Universidad de Yale. Principalmente proporciona implementación de inicio de sesión único para proyectos web y pertenece a Web SSO .

2.2 arquitectura CAS

La arquitectura CAS se divide en servidor CAS y cliente CAS.

CAS Server es Cas de código abierto, debe ir a github para descargar y luego modificar; Cas Client
puede ser una aplicación o web o PC, CAS admite múltiples lenguajes de desarrollo, java, php, C #, etc.

Escriba una descripción de la imagen aquí
PD: La imagen proviene del sitio web oficial. Aquí hay una breve introducción. Como puede ver en la imagen, CAS admite múltiples métodos de autenticación, uno es LDAP, la base de datos más común JDBC de base de datos y Active Directory, etc. Los protocolos admitidos son Protocolo personalizado, CAS, OAuth, OpenID, API RESTful, SAML1.1, SAML2.0, etc.

2.3 Principio CAS

La siguiente es una imagen del
Escriba una descripción de la imagen aquí
inicio de sesión oficial de CAS de CAS y otros sistemas se dividen en Servidor CAS y Cliente CAS, a continuación, explico un poco de acuerdo a mi entendimiento:

1. El usuario accede al cliente CAS para solicitar recursos

2. El programa cliente redirigido al servidor CAS

3. El servidor CAS autenticará la solicitud y verificará si hay un TGC (Cookie de boleto otorgado). Si hay un TGC, significa que ya ha iniciado sesión. No necesita iniciar sesión nuevamente. Si no lo hace, volverá a la página de inicio de sesión

4. Después de pasar la autenticación, se generará un Boleto de servicio y se devolverá al Cliente Cas. El cliente almacena en caché el boleto, que generalmente se coloca en una cookie. Lo llamamos TGC (Cookie de boleto otorgado)

5. Luego, el Cliente Cas toma el Boleto para visitar el Servidor Cas nuevamente, y el Servidor CAS realiza la verificación del boleto

6. El servidor CAS verifica el ticket y devuelve la información del usuario después de pasar. El usuario puede iniciar sesión después de recibir la información

Al ver este proceso, probablemente podamos entender cómo se implementa CAS. Parece que hay muchos procesos, pero CAS los realiza en segundo plano. La comunicación entre el servicio CAS y el cliente CAS se basa en HttpUrlConnection

Puntos a tener en cuenta:

  • TGT (Ticket Granded Ticket) es una cookie que almacena credenciales de autenticación. TGT indica que se ha autenticado
  • ST (Ticket de servicio) es un ticket único e inolvidable generado por el centro de certificación CAS, utilizado para la autenticación
  • Si no ha iniciado sesión o el TGT no es válido, también irá al centro de autenticación cuando lo visite. Si descubre que no hay TGT, significa que no ha pasado la autenticación. Será redirigido a la página de inicio de sesión directamente. Después de ingresar la contraseña de la cuenta, será redirigido nuevamente al centro de autenticación. ST, volver al cliente y guardar en TGC
  • Si ha iniciado sesión y el TGT no ha expirado, llévelo directamente al centro de certificación para obtener la certificación. El centro de certificación encuentra el TGT, lo redirige al cliente y toma el ST, y el cliente lleva el ST al centro de certificación para su verificación

3. Construcción del servidor CAS

3.1 CAS admite la configuración de inicio de sesión HTTP

De manera predeterminada, CAS requiere un enlace https para iniciar sesión, pero si aprende, primero puede controlar las restricciones https. Este blog presenta Cas4.2.7, que se modificó desde 4.0 antes. Para obtener más información, consulte https://blog.csdn.net/u014427391 / artículo / detalles / 82083995

La modificación de Cas4.2.7 y 4.0 es diferente. La versión Cas4.2.7 necesita ser compilada por sí misma. Está basada en Gradle, no en Maven. Si se siente problemático, puede descargar 4.0, porque la versión 4.0 proporciona un paquete de guerra, no necesita compilarlo usted mismo. Presente la versión 4.2.7, cómo admitir el inicio de sesión http

Necesidad de modificar cas4.2.7 cas-server-webapp / WEB-INF / cas.properties, todo cambiado a no seguro

tgc.secure=false
warn.cookie.secure=false

cas-server-webapp / resources / service / HTTPSandIMAPS-10000001.json

"serviceId" : "^(https|imaps)://.*"

Más http

"serviceId" : "^(https|imaps|http)://.*"

Comente en la página cas-server-webapp / WEB-INF / view / jsp / default / ui / casLoginView.jsp para verificar si es un bloque de etiqueta del protocolo HTTPS

<c:if test="${not pageContext.request.secure}">
    <div id="msg" class="errors">
        <h2><spring:message code="screen.nonsecure.title" /></h2>
        <p><spring:message code="screen.nonsecure.message" /></p>
    </div>
</c:if>

Luego, inicie sesión no hay un mensaje que no sea de seguridad

3.2 Implementación y operación del servidor CAS

Luego, suelte el paquete war en la aplicación web Tomcat, comienza la implementación, la contraseña predeterminada de la cuenta casuser / Mellon, la contraseña de la cuenta cas4.2.7 está escrita en cas.properties, esto es diferente de 4.0

accept.authn.users=casuser::Mellon

Inicio de sesión único

El inicio de sesión es exitoso. Por supuesto, no puede hacer esto en el proyecto. Esto requiere que configuremos jdbc o agreguemos verificación de permisos, etc.
Inserte la descripción de la imagen aquí
Cierre de sesión único, el enlace es http://127.0.0.1:8080/cas/logout
Inserte la descripción de la imagen aquí

4. Acceso de cliente CAS

Este blog presenta el acceso del cliente Cas basado en SpringBoot, la base de datos usa mysql y el control de permisos usa Shiro

Configuración de Maven, más tarros relacionados con Shiro y CAS:

Versiones relacionadas de CAS y Shiro

<properties>
        <shiro.version>1.2.3</shiro.version>
        <shiro.spring.version>1.2.4</shiro.spring.version>
        <shiro.encache.version>1.2.4</shiro.encache.version>
        <cas.version>3.2.0</cas.version>
        <shiro.cas.version>1.2.4</shiro.cas.version>
    </properties>

Además de frascos relacionados con Shiro y cas

 <!-- Shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.encache.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-cas</artifactId>
            <version>${shiro.cas.version}</version>
        </dependency>

		<!-- cas -->
        <dependency>
            <groupId>org.jasig.cas.client</groupId>
            <artifactId>cas-client-core</artifactId>
            <version>${cas.version}</version>
        </dependency>

Cree una nueva clase de entorno y guarde algunos enlaces de configuración de cas:

package org.muses.jeeplatform.core;

/**
 * <pre>
 *  CAS配置环境类
 * </pre>
 *
 * @author nicky.ma
 * <pre>
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2019年05月25日  修改内容:
 * </pre>
 */
public class CASConsts {

    /* CAS单点登录配置 */
    //Cas server地址
    public static final String CAS_SERVER_URL_PREFIX = "http://localhost:8080/cas";
    //Cas单点登录地址
    public static final String CAS_LOGIN_URL = CAS_SERVER_URL_PREFIX +"/login";
    //CAS单点登出地址
    public static final String CAS_LOGOUT_URL = CAS_SERVER_URL_PREFIX + "/logout";
    //对外提供的服务地址
    public static final String SERVER_URL_PREFIX = "http://localhost:8081";
    //Cas过滤器的urlPattern
    public static final String CAS_FILTER_URL_PATTERN = "/jeeplatform";
    //CAS客户端单点登录跳转地址
    public static final String CAS_CLIENT_LOGIN_URL = CAS_LOGIN_URL + "?service="+SERVER_URL_PREFIX+CAS_FILTER_URL_PATTERN;
    //CAS客户端单点登出
    public static final String CAS_CLIENT_LOGOUT_URL = CAS_LOGOUT_URL + "?service="+SERVER_URL_PREFIX+CAS_FILTER_URL_PATTERN;
    //登录成功地址
    public static final String LOGIN_SUCCESS_URL = "/index";
    //无权访问页面403
    public static final String LOGIN_UNAUTHORIZED_URL = "/403";

}

Clase de configuración de ShiroCas:

package org.muses.jeeplatform.config;

import org.apache.shiro.cas.CasFilter;
import org.apache.shiro.cas.CasSubjectFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
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.validation.Cas20ProxyReceivingTicketValidationFilter;
import org.muses.jeeplatform.core.shiro.ShiroRealm;
import org.muses.jeeplatform.web.filter.SysAccessControllerFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import static org.muses.jeeplatform.core.CASConsts.*;

/**
 * @author nicky.ma
 */
@Configuration
public class ShiroConfig {

    private static final Logger LOG = LoggerFactory.getLogger(ShiroConfig.class);

    /**
     *  单点登出监听器
     * @return
     */
    @Bean
    public ServletListenerRegistrationBean singleSignOutHttpSeessionListener(){
        ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean();
        bean.setListener(new SingleSignOutHttpSessionListener());
        bean.setEnabled(true);
        return bean;
    }

    /**
     * 注册单点登出的过滤器
     * @return
     */
    @Bean
    public FilterRegistrationBean singleSignOutFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setName("singleSignOutFilter");
        bean.setFilter(new SingleSignOutFilter());
        bean.addUrlPatterns("/*");
        bean.setEnabled(true);
        return bean;
    }

    @Bean
    public FilterRegistrationBean authenticationFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new AuthenticationFilter());
        bean.addUrlPatterns("/*");
        bean.setName("CAS AuthenticationFilter");
        bean.addInitParameter("casServerLoginUrl",CAS_SERVER_URL_PREFIX);
        bean.addInitParameter("serverName",SERVER_URL_PREFIX);
        return bean;
    }

    /**
     * 单点登录校验
     * @return
     */
    @Bean
    public FilterRegistrationBean validationFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new Cas20ProxyReceivingTicketValidationFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setName("CAS Validation Filter");
        registrationBean.addInitParameter("casServerUrlPrefix", CAS_SERVER_URL_PREFIX );
        registrationBean.addInitParameter("serverName", SERVER_URL_PREFIX );
        return registrationBean;
    }


    /**
     * CAS过滤器
     * @return
     */
    @Bean
    public CasFilter getCasFilter(){
        CasFilter casFilter = new CasFilter();
        casFilter.setName("casFilter");
        casFilter.setEnabled(true);
        casFilter.setFailureUrl(CAS_CLIENT_LOGIN_URL);
        casFilter.setSuccessUrl(LOGIN_SUCCESS_URL);
        return casFilter;
    }

    /**
     * 定义ShrioRealm
     * @return
     */
    @Bean
    public ShiroRealm myShiroRealm(){
        ShiroRealm myShiroRealm = new ShiroRealm();
        return myShiroRealm;
    }

    /**
     * Shiro Security Manager
     * @return
     */
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        //securityManager.setRealm(myShiroRealm());
        securityManager.setSubjectFactory(new CasSubjectFactory());
        return securityManager;
    }

    /**
     * ShiroFilterFactoryBean
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager,CasFilter casFilter) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //注册Shrio Security Manager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        shiroFilterFactoryBean.setLoginUrl(CAS_CLIENT_LOGIN_URL);
        shiroFilterFactoryBean.setSuccessUrl(LOGIN_SUCCESS_URL);
        shiroFilterFactoryBean.setUnauthorizedUrl(LOGIN_UNAUTHORIZED_URL);

        //添加CasFilter到ShiroFilter
        Map<String,Filter> filters = new HashMap<String,Filter>();
        filters.put("casFilter",casFilter);
        shiroFilterFactoryBean.setFilters(filters);

        //拦截器.
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
        //Shiro集成CAS后需要添加该规则
        filterChainDefinitionMap.put(CAS_FILTER_URL_PATTERN,"casFilter");
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/upload/**", "anon");
        filterChainDefinitionMap.put("/plugins/**", "anon");
        filterChainDefinitionMap.put("/code", "anon");
        //filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/403", "anon");
        //filterChainDefinitionMap.put("/logincheck", "anon");
        filterChainDefinitionMap.put("/logout","anon");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

   
}

CasRealm personalizado:

package org.muses.jeeplatform.core.shiro;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cas.CasRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.muses.jeeplatform.core.entity.admin.User;
import org.muses.jeeplatform.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import static org.muses.jeeplatform.core.CASConsts.CAS_FILTER_URL_PATTERN;
import static org.muses.jeeplatform.core.CASConsts.CAS_SERVER_URL_PREFIX;

/**
 * @description 基于Shiro框架的权限安全认证和授权
 * @author Nicky
 * @date 2017年3月12日
 */
public class ShiroRealm extends CasRealm {

	Logger LOG = LoggerFactory.getLogger(ShiroRealm.class);

	/**注解引入业务类**/
	@Resource
	UserService userService;

	@PostConstruct
	public void initProperty(){
		setCasServerUrlPrefix(CAS_SERVER_URL_PREFIX);
		//客户端回调地址
		setCasService(CAS_SERVER_URL_PREFIX + CAS_FILTER_URL_PATTERN);
	}
	
	/**
	 * 登录信息和用户验证信息验证(non-Javadoc)
	 * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(AuthenticationToken)
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

		if(LOG.isInfoEnabled()) {
			LOG.info("=>执行Shiro权限认证");
		}

		String username = (String)token.getPrincipal();  				//得到用户名
		String password = new String((char[])token.getCredentials()); 	//得到密码
	     
		User user = userService.findByUsername(username);

		/* 检测是否有此用户 */
		if(user == null){
			throw new UnknownAccountException();//没有找到账号异常
		}
		/* 检验账号是否被锁定 */
		if(Boolean.TRUE.equals(user.getLocked())){
			throw new LockedAccountException();//抛出账号锁定异常
		}
		/* AuthenticatingRealm使用CredentialsMatcher进行密码匹配*/
		if(null != username && null != password){
			return new SimpleAuthenticationInfo(username, password, getName());
		}else{
	    	 return null;
		}

	}
	
	/**
	 * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc)
	 * @see AuthorizingRealm#doGetAuthorizationInfo(PrincipalCollection)
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
		if(LOG.isInfoEnabled()) {
			LOG.info("=>执行Shiro授权");
		}
		String username = (String)pc.getPrimaryPrincipal();
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
	    authorizationInfo.setRoles(userService.getRoles(username));
	    authorizationInfo.setStringPermissions(userService.getPermissions(username));
	    return authorizationInfo;
	}
	
	 @Override
	 public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
		 super.clearCachedAuthorizationInfo(principals);
	 }

	 @Override
	 public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
	     super.clearCachedAuthenticationInfo(principals);
	 }

	 @Override
	 public void clearCache(PrincipalCollection principals) {
	      super.clearCache(principals);
	 }

}

Cinco, acceso de velocidad del cliente

El ejemplo anterior es problemático. Si queremos acceder al cliente, podemos usar la librería de inicio de terceros para conectarse

 <!-- CAS依赖包 -->
        <dependency>
            <groupId>net.unicon.cas</groupId>
            <artifactId>cas-client-autoconfig-support</artifactId>
            <version>1.5.0-GA</version>
        </dependency>

configuración de yaml:

cas:
  server-login-url: http://127.0.0.1:8080/cas/login
  server-url-prefix: http://127.0.0.1:8080/cas
  client-host-url: http://127.0.0.1:8081
  validation-type: cas
#  use-session: true

Agregue una clase de configuración Springboot:

package org.muses.jeeplatform.config;

import net.unicon.cas.client.configuration.CasClientConfigurerAdapter;
import net.unicon.cas.client.configuration.EnableCasClient;
import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableCasClient
public class CASConfig extends CasClientConfigurerAdapter {

    private static final String CAS_SERVER_URL_LOGIN = "http://localhost:8080/cas/login";
    private static final String SERVER_NAME = "http://localhost:8081/";


//    @Override
//    public void configureAuthenticationFilter(FilterRegistrationBean authenticationFilter) {
//        super.configureAuthenticationFilter(authenticationFilter);
//        //authenticationFilter.getInitParameters().put("authenticationRedirectStrategyClass","com.test.CustomAuthRedirectStrategy");
//    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new AuthenticationFilter());
        registrationBean.addUrlPatterns("/*");
        Map<String, String> initParameters = new HashMap<String,String>(16);
        initParameters.put("casServerLoginUrl",CAS_SERVER_URL_LOGIN);
        initParameters.put("serverName",SERVER_NAME);
        initParameters.put("ignorePattern","/logoutSuccess/*");
        registrationBean.setOrder(1);
        return registrationBean;
    }


}

Supongo que te gusta

Origin www.cnblogs.com/mzq123/p/12684429.html
Recomendado
Clasificación