前言:

前后端是基于若依的前后端分离框架(RuoYi-Vue)进行搭建的,现在需要加入cas单点认证,并且支持配置文件配置的方式,动态的切换认证的方式。
cas服务器的搭建,直接在网上下载即可,本文主要是对cas客户端系统前后端项目的改造,有不当之处,希望大家指正。
特别注意,前端和后端的 casEnable配置需要一致

一 后端

1 在pom中加入依赖

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

2 修改LoginUser.java

由于CAS认证需要authorities属性,此属性不能为空,此处为了方便直接new HashSet():

    @Overridepublic Collection<? extends GrantedAuthority> getAuthorities(){return new HashSet();}

2 在application.yml添加配置

其中配置了cas的开关

3 新增 CasProperties配置类


@Component
public class CasProperties {@Value("${app.server.host.url}")private String appServerUrl;@Value("${app.server.home.url}")private String appServerHomeUrl;@Value("${app.login.url}")private String appLoginUrl;@Value("${app.logout.url}")private String appLogoutUrl;@Value("${app.key}")private String appKey;@Value("${app.casEnable}")private boolean casEnable;@Value("${cas.server.host}")private String casServerUrl;@Value("${cas.server.login_url}")private String casServerLoginUrl;@Value("${cas.server.logout_url}")private String casServerLogoutUrl;public CasProperties() {}public String getAppKey() {return appKey;}public void setAppKey(String appKey) {this.appKey = appKey;}public String getAppServerHomeUrl() {return appServerHomeUrl;}public void setAppServerHomeUrl(String appServerHomeUrl) {this.appServerHomeUrl = appServerHomeUrl;}public String getAppServerUrl() {return appServerUrl;}public void setAppServerUrl(String appServerUrl) {this.appServerUrl = appServerUrl;}public String getAppLoginUrl() {return appLoginUrl;}public void setAppLoginUrl(String appLoginUrl) {this.appLoginUrl = appLoginUrl;}public String getAppLogoutUrl() {return appLogoutUrl;}public void setAppLogoutUrl(String appLogoutUrl) {this.appLogoutUrl = appLogoutUrl;}public String getCasServerUrl() {return casServerUrl;}public boolean isCasEnable() {return casEnable;}public void setCasEnable(boolean casEnable) {this.casEnable = casEnable;}public void setCasServerUrl(String casServerUrl) {this.casServerUrl = casServerUrl;}public String getCasServerLoginUrl() {return casServerLoginUrl;}public void setCasServerLoginUrl(String casServerLoginUrl) {this.casServerLoginUrl = casServerLoginUrl;}public String getCasServerLogoutUrl() {return casServerLogoutUrl;}public void setCasServerLogoutUrl(String casServerLogoutUrl) {this.casServerLogoutUrl = casServerLogoutUrl;}
}

4 新增SysCASController用于跳转

@RestController
@RequestMapping("/cas")
public class SysCASController extends BaseController {@Autowiredprivate CasProperties casProperties;/*** 适用前后端分离* 当未登录时重定向到此请求,返回给前端CAS服务器登录地址,通过前端跳转** @return*/@GetMapping("/send")public AjaxResult send() {String url = casProperties.getCasServerLoginUrl() + "?service=" + casProperties.getAppServerUrl() + casProperties.getAppLoginUrl() + "&key=" + casProperties.getAppKey();return AjaxResult.error(600, url);}/*** 适用前后端分离* 当登录成功后返回前端数据** @return*/@GetMapping("/login")public AjaxResult login(HttpServletResponse response) throws IOException {response.sendRedirect(casProperties.getAppServerHomeUrl());return AjaxResult.success("成功");}
}

5 新增cas认证失败类CasAuthenticationEntryPointImpl

这里是直接重定向到controller的send接口回到首页,可根据业务自定义实现认证失败的逻辑


@Component
public class CasAuthenticationEntryPointImpl implements AuthenticationEntryPoint {@Autowiredprivate CasProperties casProperties;@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response,AuthenticationException authException) throws IOException {response.sendRedirect(casProperties.getAppServerUrl() + "/send");}
}

6 新增用户认证逻辑

根据自己系统内部的认证方式去自行修改,楼主这里是需要取封装全局的LoginUser对象,大家根据自己的系统去实现即可,楼楼的如下:

@Service
public class CasUserDetailsService implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {private static final Logger log = LoggerFactory.getLogger(CasUserDetailsService.class);@Autowiredprivate ISysUserService userService;@Autowiredprivate SysPermissionService permissionService;@Overridepublic UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException {String username = token.getName();SysUser user = userService.selectUserByUserName(username);if (StringUtils.isNull(user)) {log.info("登录用户:{} 不存在.", username);throw new ServiceException("登录用户:" + username + " 不存在");} else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {log.info("登录用户:{} 已被删除.", username);throw new ServiceException("对不起,您的账号:" + username + " 已被删除");} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {log.info("登录用户:{} 已被停用.", username);throw new ServiceException("对不起,您的账号:" + username + " 已停用");}return createLoginUser(user);}public UserDetails createLoginUser(SysUser user) {return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));}

7 修改SecurityConfig配置类

通过casEnable确认启用的认证方式

@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate CasProperties casProperties;@Autowiredprivate CasAuthenticationEntryPointImpl casAuthenticationEntryPoint;@Autowiredprivate CasUserDetailsService casUserDetailsService;/*** 自定义用户认证逻辑*/@Autowiredprivate UserDetailsService userDetailsService;/*** 认证失败处理类*/@Autowiredprivate AuthenticationEntryPointImpl unauthorizedHandler;/*** 退出处理类*/@Autowiredprivate LogoutSuccessHandlerImpl logoutSuccessHandler;/*** token认证过滤器*/@Autowiredprivate JwtAuthenticationTokenFilter authenticationTokenFilter;/*** 跨域过滤器*/@Autowiredprivate CorsFilter corsFilter;/*** 解决 无法直接注入 AuthenticationManager** @return* @throws Exception*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}/*** anyRequest          |   匹配所有请求路径* access              |   SpringEl表达式结果为true时可以访问* anonymous           |   匿名可以访问* denyAll             |   用户不能访问* fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)* hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问* hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问* hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问* hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问* hasRole             |   如果有参数,参数表示角色,则其角色可以访问* permitAll           |   用户可以任意访问* rememberMe          |   允许通过remember-me登录的用户访问* authenticated       |   用户登录后可访问*/@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception {if (!casProperties.isCasEnable()) {httpSecurity// CSRF禁用,因为不使用session.csrf().disable()// 认证失败处理类.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()// 基于token,所以不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()// 过滤请求.authorizeRequests()// 对于登录login 注册register 验证码captchaImage 允许匿名访问.antMatchers("/login", "/register", "/captchaImage", "/refToken").anonymous().antMatchers(HttpMethod.GET,"/","/*.html","/**/*.html","/**/*.css","/**/*.js").permitAll().antMatchers("/tool/manual**").authenticated().antMatchers("/doc.html").anonymous().antMatchers("/swagger-resources/**").anonymous().antMatchers("/webjars/**").anonymous().antMatchers("/*/api-docs").anonymous().antMatchers("/druid/**").anonymous()// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated().and().cors().and().headers().frameOptions().disable();httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);// 添加JWT filterhttpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
//            // 添加CORS filterhttpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);} else {httpSecurity// CSRF禁用,因为不使用session.csrf().disable()// 过滤请求.authorizeRequests()//.antMatchers("/refToken", "/doc.html").anonymous().antMatchers(HttpMethod.GET,"/","/*.html","/**/*.html","/**/*.css","/**/*.js").permitAll().antMatchers("/tool/manual**").authenticated().antMatchers("/cas/**").permitAll().antMatchers("/swagger-resources/**").anonymous().antMatchers("/webjars/**").anonymous().antMatchers("/*/api-docs").anonymous().antMatchers("/druid/**").anonymous()// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated().and().cors().and().logout().permitAll().and()//logout不需要验证.cors().and().headers().frameOptions().disable();httpSecurity.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint) //认证失败.and().addFilter(casAuthenticationFilter()).addFilterBefore(authenticationTokenFilter, CasAuthenticationFilter.class).addFilterBefore(casLogoutFilter(), LogoutFilter.class).addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class);httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);httpSecurity.headers().cacheControl();}}/*** 强散列哈希加密实现*/@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}/*** 身份认证接口*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {if (!casProperties.isCasEnable()) {auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());} else {auth.authenticationProvider(casAuthenticationProvider());}}/*** 主要配置的是ServiceProperties的service属性,它指定的是cas回调的地址*/@ConditionalOnExpression("${app.casEnable}")@Beanpublic ServiceProperties serviceProperties() {ServiceProperties serviceProperties = new ServiceProperties();serviceProperties.setService(casProperties.getAppServerUrl() + casProperties.getAppLoginUrl());serviceProperties.setSendRenew(false);serviceProperties.setAuthenticateAllArtifacts(true);return serviceProperties;}@ConditionalOnExpression("${app.casEnable}")@Beanpublic CasAuthenticationFilter casAuthenticationFilter() throws Exception {CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();casAuthenticationFilter.setServiceProperties(serviceProperties());casAuthenticationFilter.setFilterProcessesUrl(casProperties.getAppLoginUrl());casAuthenticationFilter.setAuthenticationManager(authenticationManager());casAuthenticationFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(casProperties.getAppServerUrl() + "/hello"));casAuthenticationFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());return casAuthenticationFilter;}@ConditionalOnExpression("${app.casEnable}")@Beanpublic CasAuthenticationProvider casAuthenticationProvider() {CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();casAuthenticationProvider.setServiceProperties(serviceProperties());casAuthenticationProvider.setTicketValidator(cas30ServiceTicketValidator());casAuthenticationProvider.setAuthenticationUserDetailsService(casUserDetailsService);casAuthenticationProvider.setKey("casAuthenticationProviderKey");return casAuthenticationProvider;}/*** 验证ticker,向cas服务器发送验证请求*/@ConditionalOnExpression("${app.casEnable}")@Beanpublic Cas30ProxyTicketValidator cas30ServiceTicketValidator() {Cas30ProxyTicketValidator cas30ServiceTicketValidator = new Cas30ProxyTicketValidator(casProperties.getCasServerUrl());cas30ServiceTicketValidator.setEncoding("UTF-8");return cas30ServiceTicketValidator;}@ConditionalOnExpression("${app.casEnable}")@Beanpublic SessionAuthenticationStrategy sessionAuthenticationStrategy() {return new SessionFixationProtectionStrategy();}/*** 此过滤器向cas发送登出请求*/@ConditionalOnExpression("${app.casEnable}")@Beanpublic SingleSignOutFilter singleSignOutFilter() {SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();singleSignOutFilter.setCasServerUrlPrefix(casProperties.getCasServerUrl());singleSignOutFilter.setIgnoreInitConfiguration(true);return singleSignOutFilter;}/*** 此过滤器拦截客户端的logout请求,发现logout请求后向cas服务器发送登出请求*/@ConditionalOnExpression("${app.casEnable}")@Beanpublic LogoutFilter casLogoutFilter() {LogoutFilter logoutFilter = new LogoutFilter(casProperties.getCasServerLogoutUrl(),new SecurityContextLogoutHandler());logoutFilter.setFilterProcessesUrl(casProperties.getAppLogoutUrl());return logoutFilter;}/*** 取出@Secured的前缀 "ROLE_"** @return*/@ConditionalOnExpression("${app.casEnable}")@Beanpublic GrantedAuthorityDefaults grantedAuthorityDefaults() {return new GrantedAuthorityDefaults("");}

从上面的配置可以看出,退出处理和token的认证类沿用之前的认证方式即可
特别注意(楼楼在这里栽了大跟头,手动狗头):
在不同模式下通过@ConditionalOnExpression注解,动态的注入bean防止bean的冲突

二 前端

前端不咋会,望大家指正,相互学习

1 在setting.js文件添加配置开关

2 修改Navbar.vue中logout方法

3 修改permission.js文件全局路由


4 修改request.js文件,添加600状态码


至此配置就结束啦

springboot springsecurity接入cas单点登录,前后端分离相关推荐

  1. SSO单点登录前后端分离完整版·(开源)

    前言:3月建的仓,完成确是在7月中旬,实际完成时间加起来不到10天,中间停滞了很久,主要工作太忙,白天没时间搞,况且工作摸鱼也不是我的作风,晚上下班回家要帮家人一起带娃,唯一的时间也会用来看看书和视频 ...

  2. 微信第三方登录前后端分离实现思路

    微信第三方登录前后端分离实现思路 前端实现 这里说一下前后端的思路,页面加载时声明一个变量state='时间戳+6位随机数', 前端路径生成二维码, 其中有个state参数需要我们传递,这个参数你传什 ...

  3. java计算机毕业设计基于springboot+vue+elementUI的实验室管理系统(前后端分离)

    项目介绍 科技水平一直是体现一个国家强弱的重要标志,而科技的一点诞生地是实验室,如果能够更好的对实验室进行管理是很多实验室管理人员一直研究的一个问题.只有更加科学和合理化的利用实验室才能够更好的让科技 ...

  4. php前后端分离登录,前后端分离下如何登录

    1 Web登录涉及到知识点 1.1 HTTP无状态性HTTP是无状态的,一次请求结束,连接断开,下次服务器再收到请求,它就不知道这个请求是哪个用户发过来的.当然它知道是哪个客户端地址发过来的,但是对于 ...

  5. 微信授权登录-前后端分离

    简介 在前后端分离情况下实现微信服务号和订阅号授权登录,项目使用springboot+Vue前后端分离的开发模式. 备注:目前只提供后端代码实现以及业务逻辑 流程图 服务号 开发逻辑 进入活动页面根据 ...

  6. 毕设:基于SpringBoot+Vue 实现在线考试系统(前后端分离)

    文章目录 一.简介 1.背景 2.项目介绍 3.难度系数 二.功能 1.系统组成(产品组成图) 2.功能介绍 学生系统管理 管理系统功能 三.核心技术 1.系统架构图 2.技术选型 后端 前端 3.系 ...

  7. 【Vue+SpringBoot】超详细!一周开发一个SpringBoot + Vue+MybatisPlus+Shiro+JWT+Redis前后端分离个人博客项目!!!【项目完结】

    项目目录 资源准备 前后端分离项目 技术栈 Java后端接口开发 1.前言 2.新建Springboot项目 3.整合mybatis plus 3.统一结果封装 4.整合shiro+jwt,并会话共享 ...

  8. 基于springboot+vue的高校迎新系统(前后端分离)

    博主主页:猫头鹰源码 博主简介:Java领域优质创作者.CSDN博客专家.公司架构师.全网粉丝5万+.专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等).简历模 ...

  9. 基于springboot+vue的电子村务系统(前后端分离)

    博主主页:猫头鹰源码 博主简介:Java领域优质创作者.CSDN博客专家.公司架构师.全网粉丝5万+.专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等).简历模 ...

  10. Vue iView Admin 动态路由菜单加载 前后端分离(springboot 2.x iview admin vue 前后端分离 模型设计器 动态数据权限...

    宣传官网 xb.exrick.cn 在线Demo xboot.exrick.cn 开源版Github地址 github.com/Exrick/x-bo- 开发文档 www.kancloud.cn/ex ...

最新文章

  1. Dependabot:自动创建GitHub PR修复潜在漏洞
  2. git pull报“unable to update local ref”解决方案
  3. ABAP Submit 用法解析
  4. eclipse中经常用到的快捷键
  5. android string数组转json_移动端开发基础【20】pages.json的配置项pages
  6. font: 12px/1.5 Tahoma, Helvetica, Arial, sans-serif;
  7. 深入浅出MFC第二章笔记
  8. 数据库系统概述--数据库习题
  9. 7 Python文件和数据格式化
  10. bzoj5369loj6433 [Pkusc2018]最大前缀和
  11. 【Mesh】关于Mesh中Seq+IV与RPL分析
  12. cadence导入dxf文件_PCB原创|cadence allegro导入DXF文件操作步骤
  13. 云计算中的存储基础知识
  14. 【译】A Fully Spiking Hybrid Neural Network for Energy-Efficient Object Detection
  15. 一篇文章读懂:Spark运行模式
  16. 三极管-【设计】三极管的功用-晶体管的电流放大作用
  17. linux 内核 风扇,解决Ubuntu 8.04下笔记本CPU风扇的问题
  18. mpf4_定价欧式美式障碍Options_CRR_Leisen-Reimer_Greeks_二叉树三叉树网格_Finite differences(显式隐式)Crank-Nicolson_Imp波动率
  19. OpenGIS 的WKB和WKT
  20. Windows 2.0 下载

热门文章

  1. 第39级台阶(递归+dp)
  2. 【FBI WARNING】好东西!!!
  3. Cloud 团队:让 TiDB 在云上跳舞 | PingCAP 招聘季
  4. 丝般顺滑!全新垃圾回收器 ZGC 初体验 | 龙蜥技术
  5. Java开发者,我到底要不要学大数据开发?
  6. linux fdisk等命令,linux命令:fdisk(示例代码)
  7. javascript初级动态效果之使用原生js实现轮播图效果
  8. Retrofit原理
  9. 虾皮测试面试——凉经
  10. 手机多控软件使用全面评测,选择更适合自己的那一款!