SpringBoot+Vue 前后端分离实现单点登录方案

springboot springsecurity接入cas单点登录,前后端分离 springsecurity接入cas单点登录并支持切换认证方式 阅读详情

文章目录

  • 前言

  • 一、CAS是什么?

  • 二、搭建客户端系统

  1. 引入CAS

  2. 客户端后端搭建

总结

前言

什么是单点登录?单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分,如图(不标准,只是方便理解)。

b9b7a434681d24a38f68bd71b9993f61.png

一、CAS是什么?

CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。CAS 具有以下特点:

  • 开源的企业级单点登录解决方案。

  • CAS Server 为需要独立部署的 Web 应用。

  • CAS Client 支持非常多的客户端(这里指单点登录系统中的各个 Web 应用),包括 Java, .Net, PHP, Perl, Apache, uPortal, Ruby 等。

二、搭建客户端系统

1.引入CAS

参考:https://www.bilibili.com/video/BV1xy4y1r7BU?t=666&p=8

注意其中将证书导入jdk中,一定要注意精确到cacerts这个文件下,不然一直报拒绝写入,另外最好用管理员下的命令窗口

2.客户端后端搭建

1.添加依赖

<dependency>
    <groupId>org.jasig.cas.client</groupId>
    <artifactId>cas-client-core</artifactId>
    <version>3.3.2</version>
</dependency>
<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.10.5</version>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-cas</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
</dependency>

2.配置客户端

server:
  port: 1234

3.添加config(filter)文件

地址全为ip,如果用hosts映射地址,可能会出现问题

package com.casclient1.cas.config;

import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.CharacterEncodingFilter;

import javax.servlet.Filter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;


@Configuration
public class FilterConfig implements Serializable, InitializingBean {

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

    public static final String CAS_SIGNOUT_FILTER_NAME = "CAS Single Sign Out Filter";
    public static final String CAS_AUTH_FILTER_NAME = "CAS Filter";
    public static final String CAS_IGNOREL_SSL_FILTER_NAME = "CAS Ignore SSL Filter";
    public static final String CAS_FILTER_NAME = "CAS Validation Filter";
    public static final String CAS_WRAPPER_NAME = "CAS HttpServletRequest Wrapper Filter";
    public static final String CAS_ASSERTION_NAME = "CAS Assertion Thread Local Filter";
    public static final String CHARACTER_ENCODING_NAME = "Character encoding Filter";

 //CAS服务器退出地址
    private static String casSigntouServerUrlPrefix = "https://127.0.0.1:8443/cas/logout";
    //CAS服务器登录地址
    private static String casServerLoginUrl = "https://127.0.0.1:8443/cas/login";
    //客户端地址
    private static String clienthosturl="http://127.0.0.1:1234";
    //CAS服务器地址
    private static String casValidationServerUrlPrefix = "https://127.0.0.1:8443/cas";

    public FilterConfig() {

    }

    /**
     * 单点登出功能,放在其他filter之前
     * casSigntouServerUrlPrefix为登出前缀:https://123.207.122.156:8081/cas/logout
     *
     * @return
     */
    @Bean
    @Order(0)
    public FilterRegistrationBean getCasSignoutFilterRegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(getCasSignoutFilter());
        registration.addUrlPatterns("/*", "*.html");
        registration.addInitParameter("casServerUrlPrefix", casSigntouServerUrlPrefix);
        registration.setName(CAS_SIGNOUT_FILTER_NAME);
        registration.setEnabled(true);
        return registration;
    }

    @Bean(name = CAS_SIGNOUT_FILTER_NAME)
    public Filter getCasSignoutFilter() {
        return new SingleSignOutFilter();
    }

    /**
     * 忽略SSL认证
     *
     * @return
     */
    @Bean
    @Order(1)
    public FilterRegistrationBean getCasSkipSSLValidationFilterRegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(getCasSkipSSLValidationFilter());
        registration.addUrlPatterns("/*", "*.html");
        registration.setName(CAS_IGNOREL_SSL_FILTER_NAME);
        registration.setEnabled(true);
        return registration;
    }

    @Bean(name = CAS_IGNOREL_SSL_FILTER_NAME)
    public Filter getCasSkipSSLValidationFilter() {
        return new IgnoreSSLValidateFilter();
    }

    /**
     * 负责用户的认证
     * casServerLoginUrl:https://123.207.122.156:8081/cas/login
     * casServerName:https://123.207.122.156:8080/tdw/alerts/
     *
     * @return
     */
    @Bean
    @Order(2)
    public FilterRegistrationBean getCasAuthFilterRegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        final Filter casAuthFilter = getCasAuthFilter();
        registration.setFilter(casAuthFilter);
        registration.addUrlPatterns("/*", "*.html");
        registration.addInitParameter("casServerLoginUrl", casServerLoginUrl);
        registration.addInitParameter("serverName", clienthosturl);
        registration.setName(CAS_AUTH_FILTER_NAME);
        registration.setEnabled(true);
        return registration;
    }

    @Bean(name = CAS_AUTH_FILTER_NAME)
    public Filter getCasAuthFilter() {
        return new MyAuthenticationFilter();
    }

    /**
     * 对Ticket进行校验
     * casValidationServerUrlPrefix要用内网ip
     * casValidationServerUrlPrefix:https://123.207.122.156:8081/cas
     * casServerName:https://123.207.122.156:8080/tdw/alerts/
     *
     * @return
     */
    @Bean
    @Order(3)
    public FilterRegistrationBean getCasValidationFilterRegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        final Filter casValidationFilter = getCasValidationFilter();
        registration.setFilter(casValidationFilter);
        registration.addUrlPatterns("/*", "*.html");
        registration.addInitParameter("casServerUrlPrefix", casValidationServerUrlPrefix);
        registration.addInitParameter("serverName", clienthosturl);
        registration.setName(CAS_FILTER_NAME);
        registration.setEnabled(true);
        return registration;
    }

    @Bean(name = CAS_FILTER_NAME)
    public Filter getCasValidationFilter() {
        return new Cas20ProxyReceivingTicketValidationFilter();
    }

    /**
     * 设置response的默认编码方式:UTF-8。
     *
     * @return
     */
    @Bean
    @Order(4)
    public FilterRegistrationBean getCharacterEncodingFilterRegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(getCharacterEncodingFilter());
        registration.addUrlPatterns("/*", "*.html");
        registration.setName(CHARACTER_ENCODING_NAME);
        registration.setEnabled(true);
        return registration;
    }

    @Bean(name = CHARACTER_ENCODING_NAME)
    public Filter getCharacterEncodingFilter() {
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        return characterEncodingFilter;
    }

    @Bean
    public FilterRegistrationBean casHttpServletRequestWrapperFilter(){
        FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
        authenticationFilter.setFilter(new HttpServletRequestWrapperFilter());
        authenticationFilter.setOrder(6);
        List<String> urlPatterns = new ArrayList<>();
        urlPatterns.add("/*");
        authenticationFilter.setUrlPatterns(urlPatterns);
        return authenticationFilter;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }
}

4.filter类中的MyAuthenticationFilter是重写cas jar包中的AuthenticationFilter,原因是CAS源码无法认证直接重定向,而ajax请求又不能直接重定向,导致前端302,而302vue response拦截器是拦截不到的。所以就想到不让cas给我重定向,给我返回状态码,告诉前端认证失败,让前端直接跳转cas服务器登录地址。

修改cas源码过滤器,复制源码AuthenticationFilter这个过滤器,重写他,其实这里只改了重定向的代码其他都一样。上MyAuthenticationFilter代码

package com.casclient1.cas.config;

import org.jasig.cas.client.authentication.*;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.ReflectUtils;
import org.jasig.cas.client.validation.Assertion;

import javax.servlet.FilterConfig;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class MyAuthenticationFilter extends AbstractCasFilter {
    private String casServerLoginUrl;
    private boolean renew = false;
    private boolean gateway = false;
    private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();
    private AuthenticationRedirectStrategy authenticationRedirectStrategy = new DefaultAuthenticationRedirectStrategy();
    private UrlPatternMatcherStrategy ignoreUrlPatternMatcherStrategyClass = null;
    private static final Map<String, Class<? extends UrlPatternMatcherStrategy>> PATTERN_MATCHER_TYPES = new HashMap();

    public MyAuthenticationFilter() {
    }

    @Override
    protected void initInternal(FilterConfig filterConfig) throws ServletException {
        if (!this.isIgnoreInitConfiguration()) {
            super.initInternal(filterConfig);
            this.setCasServerLoginUrl(this.getPropertyFromInitParams(filterConfig, "casServerLoginUrl", (String)null));
            this.logger.trace("Loaded CasServerLoginUrl parameter: {}", this.casServerLoginUrl);
            this.setRenew(this.parseBoolean(this.getPropertyFromInitParams(filterConfig, "renew", "false")));
            this.logger.trace("Loaded renew parameter: {}", this.renew);
            this.setGateway(this.parseBoolean(this.getPropertyFromInitParams(filterConfig, "gateway", "false")));
            this.logger.trace("Loaded gateway parameter: {}", this.gateway);
            String ignorePattern = this.getPropertyFromInitParams(filterConfig, "ignorePattern", (String)null);
            this.logger.trace("Loaded ignorePattern parameter: {}", ignorePattern);
            String ignoreUrlPatternType = this.getPropertyFromInitParams(filterConfig, "ignoreUrlPatternType", "REGEX");
            this.logger.trace("Loaded ignoreUrlPatternType parameter: {}", ignoreUrlPatternType);
            if (ignorePattern != null) {
                Class<? extends UrlPatternMatcherStrategy> ignoreUrlMatcherClass = (Class)PATTERN_MATCHER_TYPES.get(ignoreUrlPatternType);
                if (ignoreUrlMatcherClass != null) {
                    this.ignoreUrlPatternMatcherStrategyClass = (UrlPatternMatcherStrategy) ReflectUtils.newInstance(ignoreUrlMatcherClass.getName(), new Object[0]);
                } else {
                    try {
                        this.logger.trace("Assuming {} is a qualified class name...", ignoreUrlPatternType);
                        this.ignoreUrlPatternMatcherStrategyClass = (UrlPatternMatcherStrategy)ReflectUtils.newInstance(ignoreUrlPatternType, new Object[0]);
                    } catch (IllegalArgumentException var6) {
                        this.logger.error("Could not instantiate class [{}]", ignoreUrlPatternType, var6);
                    }
                }

                if (this.ignoreUrlPatternMatcherStrategyClass != null) {
                    this.ignoreUrlPatternMatcherStrategyClass.setPattern(ignorePattern);
                }
            }

            String gatewayStorageClass = this.getPropertyFromInitParams(filterConfig, "gatewayStorageClass", (String)null);
            if (gatewayStorageClass != null) {
                this.gatewayStorage = (GatewayResolver)ReflectUtils.newInstance(gatewayStorageClass, new Object[0]);
            }

            String authenticationRedirectStrategyClass = this.getPropertyFromInitParams(filterConfig, "authenticationRedirectStrategyClass", (String)null);
            if (authenticationRedirectStrategyClass != null) {
                this.authenticationRedirectStrategy = (AuthenticationRedirectStrategy)ReflectUtils.newInstance(authenticationRedirectStrategyClass, new Object[0]);
            }
        }

    }

    @Override
    public void init() {
        super.init();
        CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
    }

    @Override
    public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        if (this.isRequestUrlExcluded(request)) {
            this.logger.debug("Request is ignored.");
            filterChain.doFilter(request, response);
        } else {
            HttpSession session = request.getSession(false);
            Assertion assertion = session != null ? (Assertion)session.getAttribute("_const_cas_assertion_") : null;
            if (assertion != null) {
                filterChain.doFilter(request, response);
            } else {
                String serviceUrl = this.constructServiceUrl(request, response);
                String ticket = this.retrieveTicketFromRequest(request);
                boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
                if (!CommonUtils.isNotBlank(ticket) && !wasGatewayed) {
                    this.logger.debug("no ticket and no assertion found");
                    String modifiedServiceUrl;
                    if (this.gateway) {
                        this.logger.debug("setting gateway attribute in session");
                        modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
                    } else {
                        modifiedServiceUrl = serviceUrl;
                    }

                    this.logger.debug("Constructed service url: {}", modifiedServiceUrl);

                    String xRequested =request.getHeader("x-requested-with");
                    if("XMLHttpRequest".equals(xRequested)){
                        response.getWriter().write("{\"code\":202, \"msg\":\"no ticket and no assertion found\"}");
                    }else{
                        String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, this.getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
                        this.logger.debug("redirecting to \"{}\"", urlToRedirectTo);
                        this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
                    }
                } else {
                    filterChain.doFilter(request, response);
                }
            }
        }
    }

    public final void setRenew(boolean renew) {
        this.renew = renew;
    }

    public final void setGateway(boolean gateway) {
        this.gateway = gateway;
    }

    public final void setCasServerLoginUrl(String casServerLoginUrl) {
        this.casServerLoginUrl = casServerLoginUrl;
    }

    public final void setGatewayStorage(GatewayResolver gatewayStorage) {
        this.gatewayStorage = gatewayStorage;
    }

    private boolean isRequestUrlExcluded(HttpServletRequest request) {
        if (this.ignoreUrlPatternMatcherStrategyClass == null) {
            return false;
        } else {
            StringBuffer urlBuffer = request.getRequestURL();
            if (request.getQueryString() != null) {
                urlBuffer.append("?").append(request.getQueryString());
            }

            String requestUri = urlBuffer.toString();
            return this.ignoreUrlPatternMatcherStrategyClass.matches(requestUri);
        }
    }

    static {
        PATTERN_MATCHER_TYPES.put("CONTAINS", ContainsPatternUrlPatternMatcherStrategy.class);
        PATTERN_MATCHER_TYPES.put("REGEX", RegexUrlPatternMatcherStrategy.class);
        PATTERN_MATCHER_TYPES.put("EXACT", ExactUrlPatternMatcherStrategy.class);
    }
}

测试Controller

package com.casclient1.cas.controller;

import com.casclient1.cas.domain.UserDomain;
import com.casclient1.cas.tools.Result;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Console;
import java.io.IOException;

@Controller
public class TestController {
    /**
     * 测试
     * @return
     */
    @GetMapping("/test")
    @ResponseBody
    public Result<UserDomain>  login(HttpServletRequest httpServletRequest){
        System.out.println("sss");
        return new Result<>(new UserDomain(httpServletRequest.getRemoteUser()));
    }

    @GetMapping("/checkTicket")
    public void index(HttpServletResponse response) throws IOException {
        // 前端页面地址
        response.sendRedirect("http://127.0.0.1:8088/Home");
    }
        /**
     * 注销
     * @return
     */
    @RequestMapping("/logout")
    public String logout(){
        return "redirect:https://127.0.0.1:8443/cas/logout";
    }
    }

3.前端

<template xmlns="http://www.w3.org/1999/html">
  <div >

    <header style="height: 60px">
      <span>客户端2验证:{{name}}</span>
        <button @click="logout">安全退出</button>
    </header>
    <router-view></router-view>
    <!--

  <my-vue v-bind:lineID="lineID"></my-vue>-->
  </div>
</template>
<style lang="scss">
</style>
<script type="text/ecmascript-6">

  export default {

    data() {

      return {
            name:'ss'
      }

    },
mounted(){
    var _this = this;
    this.$http.get('/test', {headers: {'x-requested-with': 'XMLHttpRequest'}})
            .then(function (response) {
                console.log("sss");
                if (response.data.code === 202) {
                    debugger
                    console.log("sss");
                    window.location.href = "http://127.0.0.1:1235/checkTicket"
                } else if (response.data.code === 200) {
                    console.log("sss");
                    _this.name = response.data.data.name
                }
                console.log(response);
            })
            .catch(function (error) {
                console.log(error);
            });
        },
      methods: {
          logout() {
              window.location.href = "http://127.0.0.1:1234/logout"

          },
      }
  }
</script>

5.效果

未登录:

aca4959d0f2357e66096b612048ba940.png

点击客户端1超链接

38881b9e49eb86334d97e22bea58942d.png

登录成功

7ce2d881f3d64c70e5a4df1ab3cdef64.png

点击客户端2超链接,直接进入,无需登录

6ea364e4b2584956cdd5c01eb602dd37.png

退出

365b7986fc0b27f0ce7a257d8d2439e8.png

总结

网上有很多CAS单点登录的demo,但是对于前后端分离讲的比较详细的很少,前后端分离,必定会出现跨域,导致CAS登录无法重定向等等原因,结合和网上一些想法和部门代码后,大致做了一个比较完善,但很基础的单点登录系统,当然单点登录不光有CAS,还有JWT(1.所有服务靠约定来生成token,2.要么集中生成集中判断,所有服务都能生成都认这个,要么一个服务管控全局),OAuth2等等。

来源:blog.youkuaiyun.com/weixin_43483911/article/details/117811270

SpringBoot前后台分离项目使用Redis实现SSO单点登录 采用web集群进行的服务配置,可以得到较好的处理性能,同时也可以保证高可用的机制,但是有一个最关键性的问题就是在于Session共享与分配,从早期的开发来讲,Session共享是在tomcat中配置的(Tomcat提供了一个扩展的支持)。单点登录(SSO,Single Sign On),是在企业内部多个应用系统(如考勤系统、财务系统、人事系统等)场景下,用户只需要登录一次,就可以访问多个应用系统。在这个简化的例子中,我们是通过自定义的拦截器来处理登录,并使用Redis来存储用户的登录状态。 阅读详情

相关推荐

SpringBoot+Vue+CAS 前后端分离实现单点登录方案

文章目录 前言 一、CAS是什么? 二、搭建客户端系统 引入CAS 客户端后端搭建 总结 前言 什么是单点登录?单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分,如图(不标准,只是方便理解)。 一、CAS是什么? CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。CAS

fly1north的博客 2286

springboot+vue实现单点登录

对于每个应用系统,我们需要在Spring Boot项目中进行配置,以支持SSO,并且需要在Vue项目中进行相应的跳转和验证。总的来说,实现单点登录需要结合Spring Boot和Vue框架进行开发,其中Spring Security提供了大量的安全机制,可以帮助我们简化开发,实现单点登录和用户认证授权等功能。如果用户没有登录,需要跳转到登录页面进行登录,否则直接跳转到对应的页面。3.在Vue项目中,使用Axios库向授权API发送请求,验证用户的身份信息,获取相应的授权信息。

qq_56921846的博客 1328

使用kisso单点登录示例-前后端分离

源码地址:https://gitee.com/fanling521/kisso-demo-project 主要技术栈 Spring Boot Kisso vue.js 实践目标 前端分离后,实现单点登录 前端(postman测试) A前端:系统节点 B前端:模块1节点 C前端:模块2节点(略) 后端 A后端:认证、系统、登录节点,颁发kisso认证的cookie B后端:模块1的后端服务,添加了拦截器判断。 测试(预期目标) POSTMAN1(主节点、登录) POST http://localh

爱码士的博客 612

前后端分离项目(springboot+vue)接入单点登录cas

前后端分离单点登录cas解决方案

u011322072的博客 6797

SprinBoot(SpringSecurity)+前后端分离 集成CAS单点登录

本文介绍了基于CAS实现前后端分离单点登录的完整方案。项目采用SpringBoot 2.3.12集成SpringSecurity和CAS 6.3,前端使用Vue2。通过分析CAS与SpringSecurity的认证流程,详细阐述了前后端分离架构下的CAS实现思路,包括后端配置、SpringSecurity改造等关键步骤。重点解决了传统CAS方案在前后端分离场景中的适配问题,提供了完整的代码实现参考,证明CAS完全适用于前后端分离架构。

从对比中寻找伤害 1416

(自用2)代码笔记——spring boot 拦截器 + vue axios + 单点登录 + JWT

内容介绍:spring boot拦截器做登录验证,并实现单点登录功能(分布式项目中); 目录 一、springboot中使用拦截器(interceptor)的相关知识点 二、spring boot + vue 前后端分离跨域问题的解决: 三、拦截器和跨域配置同时使用,跨域配置失效的解决方案: 四、spring boot + vue 单点登录的实现 !!!!注意: 一、springboot中使用拦截器(interceptor)的相关知识点 首先,先介绍最基本的...

heryjisc的博客 1014

基于SpringBoot和Vue实现CAS单点登录

本文介绍了基于CAS协议实现单点登录(SSO)的科研管理系统项目。该系统原采用JWT令牌登录,现需对接学校网上办事大厅实现统一认证。文章详细阐述了CAS单点登录的原理与核心流程,包括登录流程、票据验证流程和注销流程,并通过"公司登记处"的生动类比帮助理解。在实现方面,提供了后端CAS配置类、前端跳转逻辑及业务代码示例,重点解决了前后端分离项目中的重定向问题。作者特别强调CAS票据验证必须由后端完成,前端仅处理最终凭证,并分析了直接重定向到前端的方案为何不可行。最后展示了系统接入单点登录后

27届Java后端炒粉练习生,AICoding中........ 1222

Keycloak 21.1.2 SAML2.0 集成实战:SpringBoot 3.2 + Vue 3 前后端分离项目 5 步配置

本文详细介绍了如何通过5步配置实现Keycloak 21.1.2与SpringBoot 3.2和Vue 3的SAML2.0集成,构建企业级单点登录(SSO)解决方案。内容涵盖环境准备、SpringBoot后端配置、Vue前端集成、前后端联调及生产环境优化,帮助开发者快速掌握安全认证体系搭建。

aoe41606的博客 347

SpringBoot CAS 前后端分离实现

"&" : "?");result.put("message", "未认证,请前往登录");

weixin_43729205的博客 281

使用springboot结合vue实现sso单点登录

主要为大家详细介绍了如何使用springboot+vue实现sso单点登录,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

视觉语言模型对抗攻击与防御:从传统方法到多模态挑战

本文深入探讨了视觉语言模型(VLM)面临的对抗攻击与防御挑战,从传统单模态攻击到多模态威胁的演进。分析了包括FGSM、PGD等经典攻击方法,以及新兴的多模态攻击如跨模态对抗样本和越狱攻击。同时提出了跨模态一致性检查、动态防御机制等创新防御策略,并结合医疗影像诊断等实战案例,为提升AI系统安全性提供了重要见解。

weixin_29231725的博客 393

demo(二)SpringBoot+dubbo+Spring Sercurity+VUE实例----(后端)

下面写个简单的token单点登录系统来验证下。当访问系统未登录时,跳转到登录页,登录后进入首页,左侧导航栏展示默认菜单(首页)和有权限的菜单。 关于session和token的使用,网上争议一直很大。总的来说争议在这里: (1)session是空间换时间,而token是时间换空间。session占用空间,但是可以管理过期时间,token管理部了过期时间,但是不占用空间. (2)sessionI...

w_t_y_y的博客 958

ROS2前置基础教程 | 使用CMakeLists.txt编译ROS2节点

答案是有的,那就是cmake工具。cmake通过调用CMakeLists.txt直接生成Makefile。 2.新建CMakeLists.txt 在新建,输入下面内容。 3.编译代码 我们一般会创建一个新的目录,运行cmake并进行编译,这样的好处是不会显得那么乱。 创建好文件夹,接着运行cmake指令,代表到上级目录找。 运行完cmake你应该可以在build目录下看到cmake自动生成的Makefile了,接着就可以运行make指令进行编译 运行完上面的指令,就可以在build目录下发现节点了。ROS2

生活不止眼前的苟且 1万+

SpringBoot 前后端分离项目接入 CAS 单点登录,解决跨域方案

本文探讨了在Spring Boot前后端分离项目中集成CAS单点登录的完整方案。首先介绍了CAS协议的核心机制和工作流程,分析了前后端分离架构带来的认证挑战,特别是跨域问题。随后提出了三种解决方案:CORS全局配置、Nginx反向代理和JSONP(不推荐),并详细阐述了基于CORS和Token认证的最佳实践。文章提供了完整的代码实现,包括Spring Boot后端的JWT生成与验证、CAS票据验证接口,以及Vue前端的路由守卫和回调处理。最后总结了常见问题的解决方案,并强调了安全性注意事项,如HTTPS强制

油墨香^-^的博客 824

C语言面试,8道一定会遇到的----关于指针的笔试题详解

C语言面试,8道一定会遇到的----关于指针的笔试题详解

m0_55322924的博客 463

物联网开发笔记(34)- 使用Micropython开发ESP32开发板之控制步进电机(28BYJ-48)

使用Micropython开发ESP32开发板之控制步进电机(28BYJ-48)

zhusongziye的博客 1万+

springboot +vue+ 集成cas

springboot+vue 集成cas

liuzhen917的专栏 795

别再只会setStyleSheet了!Qt实现背景透明的5种方法全解析(附性能对比)

本文深入解析Qt实现背景透明的5种高阶方案,包括QPalette、setWindowOpacity、QGraphicsOpacityEffect、样式表和复合透明方案,并提供详细的性能对比与优化建议。针对不同平台和场景,帮助开发者选择最适合的透明效果实现方式,提升UI设计质量与性能表现。

weixin_27061475的博客 260
上一篇: 高颜值:Redis官方可视化工具,功能强大!
下一篇: SpringBoot 官方推荐,连接池,太快了!
Java思维导图
博客等级 码龄10年 306粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值