BladeX相关

2024-01-19 08:30
文章标签 相关 bladex

本文主要是介绍BladeX相关,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

项目基础技术

  1. springcloud
  2. springse curity oauth2
  3. spring boot starter

整体权限框架

  1. 基于spring security oauth2
  2. 认证服务器: blade-auth,配置类
  3. 资源服务器:需要认证的模块项目
  4. 网关过滤,AuthFilter执行路径拦截和放行,基于jjwt读取token中的信息

技术点

  1. 继承HandlerMethodArgumentResolver(controller统一入参拦截器)
    在这里插入图片描述
    在这里插入图片描述
    可以直接在controller写参数来获取当前登录用户

系统模块

登录界面
在这里插入图片描述
1. 验证码获取

http://localhost:1888/api/blade-auth/oauth/captcha
  1. 自定义接口,BladeTokenEndPoint
  2. 网关放行,资源服务器放行
  3. 将key(uuid)与生成验证码存入redis,登录时候需要根据key去查code进行匹配

2. 用户登录
使用spring security oauth2实现,登录接口
在这里插入图片描述

  1. BasicAuthenticationFilter
    1.1 Basic认证是一种较为简单的HTTP认证方式,客户端通过明文(Base64编码格式)传输用户名和密码到服务端进行认证,通常需要配合HTTPS来保证信息传输的安全。这里会解析请求头中的Authorization的值
    1.2 解析完后,一系列调用到BladeClientDetailsServiceImpl,客户端信息实现类,这个类没有做其他逻辑处理,继承JdbcClientDetailsService,直接调用父类的客户端认证,查询sql有数据后返回
  2. TokenEndPoint
    2.1 进入/oauth/token,取出客户端对象封装的principal,其中的clientId,又一次执行上面1.2的逻辑,这次返回的对象信息更多,主要需要scope、grant_type,封装返回TokenRequest对象
    2.2 验证TokenRequest的scope值是否包含请求参数中的scope
    2.3 判断是否为简易模式、授权码模式、刷新token模式
    2.4 获取所有的tokenGranter,实现类CompositeTokenGranter,成员变量List,维护了两个对象,AuthorizationServerEndpointConfigurer和CaptchaTokenGranter,第一个对象维护了5个内置模式,第二个对象维护的就是我们自定义的验证码模式。遍历所有模式直到grant_type与传入的grant_type匹配成功,调用getOAuth2Authentication验证码逻辑
  3. CaptchaTokenGranter
    3.1 操作redis执行验证码判断,判断成功主动调用authenticationManager.authenticate(userAuth)进行用户认证
    3.2 DaoAuthenticationProvider的retrieveUser方法,会调用自定义的用户验证逻辑BladeUserDetailsServiceImpl.loadUserByUsername(username)
    3.3 BladeUserDetailsServiceImpl
    查询租户信息,成功后根据用户名密码和租户ID,去查询是否有用户,最后返回BladeUserDetails(继承自spring security内置User对象)
  4. DefaultTokenServices
    4.1 该类由2.4的CompositeTokenGranter对象调用。3.3对象返回后,createAccessToken传入对象调用createAccessToken方法。首先创建refresh_token,再次查询数据库client信息,取刷新令牌过期时间过期时间,算出refesh_token过期时间以及创建UUID作为DefaultOAuth2RefreshToken对象的参数。
    4.2 创建完refresh_token之后,创建关键的access_token,同4.1一样,不过取的是access_token的过期时间。封装对象OAuth2AccessToken,包含了access_token的UUID、DefaultOAuth2RefreshToken、tokenType(比如bearer)、scope、过期时间。UUID作为JTI,用于防止重放攻击
    4.3 createAccessToken方法的最后,判断是否配置了增强token类(项目中配置了BladeJwtTokenEnhancer),调用BladeJwtTokenEnhancer.enhance,加入了自定义数据。
    最后,一个内置的enhance对象JwtAccessTokenConverter,它的enhance方法,将之前的所有参数作为jwt的claims,组成标准的jwt三部分。生成access_token和refresh_token的JWT(替换掉之前的UUID)
    至此整个TokenEndPoint的/oauth/token逻辑就走完了,由于用的是JWT模式,所以不在服务器端保存生成的jwt,最后将对象返回客户端

缓存

系统集成了redis缓存
使用缓存:

@Cacheable(cacheNames = {DICT_CACHE},key="'sys:ld:dic:' + #code")

清除缓存

@CacheEvict(cacheNames = {DICT_CACHE}, allEntries = true)
  1. 访问系统管理==>系统字典==>某一父级字典名
    请求接口为
http://localhost:1888/api/blade-system/dict/child-list?current=1&size=10&parentId=1123598814738675204

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
会查询缓存(比如blade:dict::dict: id:1123598814738675204),id表示是当前字典表的父节点,存储的是Dict对象在这里插入图片描述

  1. 模块中的查询字典表缓存
    下拉框的字典表没有缓存,直接调用的是接口
    在这里插入图片描述
    查询数据的时候,接口如下
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    会从缓存中找,如下:
    在这里插入图片描述
    最后的数字是数据库保存的字典值,如果没有查到数据,会写入缓存,最后查到中文返回
  2. 修改、删除字典表
    在这里插入图片描述
    @CacheEvict(cacheNames = {DICT_CACHE}, allEntries = true)
    会删除缓存,下次查询的时候再加入缓存

日志

错误日志
在这里插入图片描述
接口日志
@ApiLog("")
通用日志
引用BladeLogger

数据权限

  1. 通过@DataAuth固定写死权限,需要写在Mapper接口上
    固定模式
    在这里插入图片描述
    在这里插入图片描述
    拦截器会获取注解上的参数值,最后组装sql,本例子是通过create_user字段来做权限,仅查看本人的数据
    在这里插入图片描述
    如果注解colum改变
    在这里插入图片描述
    在这里插入图片描述
    自定义模式
    在这里插入图片描述
    这里的括号里面的el表达式,通过下面bladeUser对象key来指定在这里插入图片描述
    在这里插入图片描述
  2. 半自动配置
    在这里插入图片描述
    需要配合web,code等于web的权限编号
    在这里插入图片描述
    这种写法不需要配置角色和数据权限绑定,类名只需要到类路径
  3. 纯web配置
    在这里插入图片描述
    在这里插入图片描述
    这种写法需要到类中的方法名上
    源码追踪
    我们访问第三种方法(在web页面配置)的接口,在Mapper接口运行之前,拦截器拦截请求
    在这里插入图片描述
    判断接口上是否有@DataAuth,有的话赋值给DataScopeModel,进入sql组装类
    在这里插入图片描述
    在getDataScopeByMapper方法,在scope-api的config中,配置了实现类在这里插入图片描述
    在这里插入图片描述
    通过当前访问接口名和角色名来获取当前角色的数据权限
    在这里插入图片描述
    返回结果之后,会存入缓存(无论是否为null,为null下次继续查询数据库)
    在这里插入图片描述
    在这里插入图片描述
    如果我们访问第二种方法(半自动配置),我们通过mapperid和roleid查不出数据,因为没有绑定roleid和数据权限(不清楚的话看第二种配置),那么会通过code来查数据
    在这里插入图片描述
    同样会存入缓存
    在这里插入图片描述
    在这里插入图片描述
    查到数据之后,会组装sql,比如当前例子为本人查看,会自动组装成where create_user = ???
    缓存存放的数据格式如下
    缓存保存的就是某一个接口和角色的管
    考虑到性能,并不是所有Mapper都会判断数据权限
    在这里插入图片描述
    在这里插入图片描述
    默认为Mapper的接口名仅包含page,Page,list,List会判断数据权限

接口权限

  1. 固定角色
    @PreAuth,里面可以写hasRole,hasAnyRole(因为函数名为这两个,第三个图)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  2. web动态配置角色
    @PreAuth(“hasPermission(‘permission_test’)”)
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    根据接口上的permission和roleid去查询,放入缓存
    @PreAuth(“permissionAll()”)
    在这里插入图片描述
    前面省略了类的路径
    根据配置的权限路径和访问的接口路径比对,如果包含此接口路径,表示有权限。
    还有其他的一些权限判断,在AuthFun里面找函数名

改造授权码模式,实现授权自定义页面

@Configuration
@AllArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {@Bean@Override@SneakyThrowspublic AuthenticationManager authenticationManagerBean() {return super.authenticationManagerBean();}@Beanpublic PasswordEncoder passwordEncoder() {return BladePasswordEncoderFactories.createDelegatingPasswordEncoder();}@Overridepublic void configure(WebSecurity web) {//Ignore, publicweb.ignoring().antMatchers("/static/**");}@Override@SneakyThrowsprotected void configure(HttpSecurity http) {http.authorizeRequests()// permitAll() 的URL路径属于公开访问,不需要权限.antMatchers("/oauth2.0/show*").permitAll().antMatchers(HttpMethod.GET, "/oauth2.0/show*").anonymous().anyRequest().authenticated();http.formLogin().loginPage("/oauth2.0/show").loginProcessingUrl("/signin").failureUrl("/oauth2.0/show?error=1").usernameParameter("username").passwordParameter("password").and().logout().logoutUrl("/signout").deleteCookies("JSESSIONID").logoutSuccessUrl("/").and().exceptionHandling();http.httpBasic().disable();}}

在这里插入图片描述

@Controller
public class OauthController {@GetMapping(value = {"/oauth2.0/show"})public String login(Model model, HttpServletRequest request) {CsrfToken csrf = (CsrfToken) request.getAttribute("_csrf");model.addAttribute("csrf", csrf.getToken());return "login";}@RequestMapping("/custom/confirm_access")public ModelAndView getAccessConfirmation(@RequestParam Map<String, Object> map, HttpServletRequest request) throws Exception {ModelAndView view = new ModelAndView();view.setViewName("grant");view.addObject("clientId",map.get("client_id"));return view;}
}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>第三方登录</title>
</head><style>.login-container {margin: 50px;width: 100%;}.form-container {margin: 0px auto;width: 50%;text-align: center;box-shadow: 1px 1px 10px #888888;height: 300px;padding: 5px;}input {margin-top: 10px;width: 350px;height: 30px;border-radius: 3px;border: 1px #E9686B solid;padding-left: 2px;}.btn {width: 350px;height: 35px;line-height: 35px;cursor: pointer;margin-top: 20px;border-radius: 3px;background-color: #E9686B;color: white;border: none;font-size: 15px;}.title {margin-top: 5px;font-size: 18px;color: #E9686B;}
</style>
<body>
<div class="login-container"><div class="form-container"><p class="title">第三方登录</p><form name="loginForm" method="post" action="http://localhost:8099/signin"><input type="text" name="username" placeholder="用户名"/><br><input type="text" name="password" placeholder="密码"/><br><input type="text" name="_csrf" placeholder="CSRF" th:value="${csrf}"/><br><button type="submit" class="btn">&nbsp;&nbsp;</button></form><p style="color: red" th:if="${param.error}">用户名或密码错误</p></div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>授权</title>
</head>
<style>html {padding: 0px;margin: 0px;}.title {background-color: #E9686B;height: 50px;padding-left: 20%;padding-right: 20%;color: white;line-height: 50px;font-size: 18px;}.title-left {float: right;}.title-right {float: left;}.title-left a {color: white;}.container {clear: both;text-align: center;}.btn {width: 350px;height: 35px;line-height: 35px;cursor: pointer;margin-top: 20px;border-radius: 3px;background-color: #E9686B;color: white;border: none;font-size: 15px;}
</style>
<body style="margin: 0px">
<div class="title"><div class="title-right">OAUTH-BOOT 授权</div><div class="title-left"><a href="#help">帮助</a></div>
</div>
<div class="container"><h3 th:text="${clientId}+' 请求授权,该应用将获取你的以下信息'"></h3><p>昵称,头像和性别</p>授权后表明你已同意 <a href="#boot" style="color: #E9686B">OAUTH-BOOT 服务协议</a><form method="post" action="/oauth/authorize"><input type="hidden" name="user_oauth_approval" value="true"><input type="hidden" name="_csrf" th:value="${_csrf.getToken()}"/><button class="btn" type="submit"> 同意/授权</button></form>
</div>
</body>
</html>

yml配置

spring:thymeleaf:prefix: classpath:/static/suffix: .html

效果图如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
收到回调后,判断states与session保存的值是否一致,防止csrf
再通过后台http调用,这里测试用的前端访问,通过code获取token
在这里插入图片描述

简易SSO搭建

用户授权基于auth项目。

  1. 编写sso-starter

@Component
public class SsoAuthenticationFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {String token = null;Cookie[] cookies = request.getCookies();for (Cookie cookie : cookies) {if (cookie.getName().equals("SSO-TOKEN")) {token = cookie.getValue();}}if (token == null) {response.sendRedirect("http://localhost:8099/sso/login");return;} else {Map<String, Object> params = new HashMap<>();params.put("token", token);String result = HttpUtil.post("http://localhost:8099/sso/checkSsoToken", new HashMap<>(), params);// 验证result
//			if(Func.equals(result,"true")){
//				filterChain.doFilter(request, response);
//			}else{
//				response.sendRedirect("http://localhost:8099/sso/login");
//				return;
//			}filterChain.doFilter(request, response);}}
}
  1. 扩展auth项目接口
@Controller
@RequestMapping("/sso")
public class SsoController {@RequestMapping("/login")public ModelAndView login(@RequestParam Map<String, Object> map, HttpServletRequest request) throws Exception {ModelAndView view = new ModelAndView();view.setViewName("ssologin");return view;}@RequestMapping("/success")public String success(@RequestParam Map<String, String> map, HttpServletRequest request, HttpServletResponse response) throws Exception {Cookie cok = new Cookie("SSO-TOKEN",map.get("token"));cok.setHttpOnly(true);cok.setPath("/");response.addCookie(cok);// 回调写死测试return "redirect:http://localhost:8080/#/workOffice/cultureBuild/board";}@RequestMapping("/checkSsoToken")public String checkSsoToken(@RequestParam Map<String, Object> map, HttpServletRequest request) throws Exception {Object token = map.get("token");return "true";}
}
  1. sso登录页面
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>sso登录</title>
</head><style>.login-container {margin: 50px;width: 100%;}.form-container {margin: 0px auto;width: 50%;text-align: center;box-shadow: 1px 1px 10px #888888;height: 300px;padding: 5px;}input {margin-top: 10px;width: 350px;height: 30px;border-radius: 3px;border: 1px #E9686B solid;padding-left: 2px;}.btn {width: 350px;height: 35px;line-height: 35px;cursor: pointer;margin-top: 20px;border-radius: 3px;background-color: #E9686B;color: white;border: none;font-size: 15px;}.title {margin-top: 5px;font-size: 18px;color: #E9686B;}
</style>
<body>
<div class="login-container"><div class="form-container"><p class="title">sso登录</p><input type="text"  id="username" name="username" placeholder="用户名"/><br><input type="text" id="password" name="password" placeholder="密码"/><br><button  class="btn" onclick="login()">&nbsp;&nbsp;</button></div><script>function login(){var username = document.getElementById("username").value;var password = document.getElementById("password").value;var xmlhttp = new XMLHttpRequest();xmlhttp.open("GET","http://localhost:8099/oauth/token?username="+username+"&password="+password+"&grant_type=captcha&scope=all&type=account&client_id=saber&client_secret=saber_secret",false);xmlhttp.send();var k = xmlhttp.responseText;// 写死token测试window.location.href="http://localhost:8099/sso/success?token=testToken";}</script>
</div>
</body>
</html>
  1. 客户端的前端需要判断ajax请求是否为重定向,如果是重定向跳到登录页面
if(response.request.responseURL == "http://localhost:8099/sso/login"){window.location.href = "http://localhost:8099/sso/login";
}

这篇关于BladeX相关的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/621827

相关文章

sqlite3 相关知识

WAL 模式 VS 回滚模式 特性WAL 模式回滚模式(Rollback Journal)定义使用写前日志来记录变更。使用回滚日志来记录事务的所有修改。特点更高的并发性和性能;支持多读者和单写者。支持安全的事务回滚,但并发性较低。性能写入性能更好,尤其是读多写少的场景。写操作会造成较大的性能开销,尤其是在事务开始时。写入流程数据首先写入 WAL 文件,然后才从 WAL 刷新到主数据库。数据在开始

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

log4j2相关配置说明以及${sys:catalina.home}应用

${sys:catalina.home} 等价于 System.getProperty("catalina.home") 就是Tomcat的根目录:  C:\apache-tomcat-7.0.77 <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" /> 2017-08-10

Node Linux相关安装

下载经编译好的文件cd /optwget https://nodejs.org/dist/v10.15.3/node-v10.15.3-linux-x64.tar.gztar -xvf node-v10.15.3-linux-x64.tar.gzln -s /opt/node-v10.15.3-linux-x64/bin/npm /usr/local/bin/ln -s /opt/nod

git ssh key相关

step1、进入.ssh文件夹   (windows下 下载git客户端)   cd ~/.ssh(windows mkdir ~/.ssh) step2、配置name和email git config --global user.name "你的名称"git config --global user.email "你的邮箱" step3、生成key ssh-keygen

zookeeper相关面试题

zk的数据同步原理?zk的集群会出现脑裂的问题吗?zk的watch机制实现原理?zk是如何保证一致性的?zk的快速选举leader原理?zk的典型应用场景zk中一个客户端修改了数据之后,其他客户端能够马上获取到最新的数据吗?zk对事物的支持? 1. zk的数据同步原理? zk的数据同步过程中,通过以下三个参数来选择对应的数据同步方式 peerLastZxid:Learner服务器(Follo

rtmp流媒体编程相关整理2013(crtmpserver,rtmpdump,x264,faac)

转自:http://blog.163.com/zhujiatc@126/blog/static/1834638201392335213119/ 相关资料在线版(不定时更新,其实也不会很多,也许一两个月也不会改) http://www.zhujiatc.esy.es/crtmpserver/index.htm 去年在这进行rtmp相关整理,其实内容早有了,只是整理一下看着方

枚举相关知识点

1.是用户定义的数据类型,为一组相关的常量赋予有意义的名字。 2.enum常量本身带有类型信息,即Weekday.SUN类型是Weekday,编译器会自动检查出类型错误,在编译期间可检查错误。 3.enum定义的枚举类有什么特点。         a.定义的enum类型总是继承自java.lang.Enum,且不能被继承,因为enum被编译器编译为final修饰的类。         b.只能定义

java计算机毕设课设—停车管理信息系统(附源码、文章、相关截图、部署视频)

这是什么系统? 资源获取方式在最下方 java计算机毕设课设—停车管理信息系统(附源码、文章、相关截图、部署视频) 停车管理信息系统是为了提升停车场的运营效率和管理水平而设计的综合性平台。系统涵盖用户信息管理、车位管理、收费管理、违规车辆处理等多个功能模块,旨在实现对停车场资源的高效配置和实时监控。此外,系统还提供了资讯管理和统计查询功能,帮助管理者及时发布信息并进行数据分析,为停车场的科学

一些数学经验总结——关于将原一元二次函数增加一些限制条件后最优结果的对比(主要针对公平关切相关的建模)

1.没有分段的情况 原函数为一元二次凹函数(开口向下),如下: 因为要使得其存在正解,必须满足,那么。 上述函数的最优结果为:,。 对应的mathematica代码如下: Clear["Global`*"]f0[x_, a_, b_, c_, d_] := (a*x - b)*(d - c*x);(*(b c+a d)/(2 a c)*)Maximize[{f0[x, a, b,