spring boot集成mybatis和springsecurity实现权限控制功能

上一篇已经实现了登录认证功能,这一篇继续实现权限控制功能,文中代码只贴出来和上一篇不一样的修改的地方,完整代码可结合上一篇一起整理spring boot集成mybatis和springsecurity实现登录认证功能-CSDN博客

数据库建表

权限控制的意思就是根据用户角色的不同,赋予不同的访问权限,比如管理员可以对数据进行增删改查操作,而普通用户只能查询数据。根据网上大部分文章的说法,最好的授权方式叫RABC,意思是基于资源的访问控制,详见SpringSecurity授权-CSDN博客

这种方式会创建5个数据库表,分别为用户表,角色表,权限表,用户角色关系表,角色权限关系表,他们之间存在一些对应关系,每次根据用户名可以查询到这个用户的角色和权限信息,我也不懂为啥要把授权相关的表拆成5个,按照我自己的理解,就是把所有用户、角色、权限信息放在一张表里也不是不行,但是既然大家都建议这么做,肯定是有道理的,所以我就按照主流方式来实现权限控制的数据库表创建

数据库建表和数据SQL如下:

-- test.permission definition权限信息表 
CREATE TABLE `permission` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `permission` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='权限信息表'; 
INSERT INTO test.permission (name,permission) VALUES ('selectinfo','url1'), ('insertinfo','url2'), ('updateinfo','url3'), ('deleteinfo','url4'); 
-- test.`role` definition角色信息表 
CREATE TABLE `role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `description` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; 
INSERT INTO test.`role` (name,description) VALUES ('admin','admin'), ('user','user');
-- test.`user` definition用户信息表 
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `permission` varchar(255) DEFAULT NULL, `role` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; 
INSERT INTO test.`user` (username,password,permission,`role`) VALUES ('aaa','$2a$10$m3tlFCB8IiZzm6dj7Q58KuD9FnkZI8M/1scJ2dZBZwiToFpgRMZCe',NULL,NULL), ('bbb','$2a$10$Wwfn31iTecY0n66LHXNSeeiPTcr.ZJsDHVuvnHR150rMqgJXugio2',NULL,NULL), ('ccc','1234',NULL,NULL); 
-- test.role_permission definition角色权限关系表 
CREATE TABLE `role_permission` ( `id` int(11) NOT NULL AUTO_INCREMENT, `role_id` varchar(255) DEFAULT NULL, `permission_id` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; 
INSERT INTO test.role_permission (role_id,permission_id) VALUES ('1','1'), ('1','2'), ('1','3'), ('1','4'), ('2','1'), ('2','3'); 
-- test.user_role definition用户角色关系表 
CREATE TABLE `user_role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` varchar(255) DEFAULT NULL, `role_id` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; 
INSERT INTO test.user_role (user_id,role_id) VALUES ('1','1'), ('1','2'), ('2','2');

所有和5个数据库表相关的dao、dto、service、mapper文件可参考上一篇user表编写,这里不在贴出源码

代码修改
1,springsecurity配置类

WebSecurityConfig添加支持权限控制的注解如下:

其中第一个配置项perPostEnable表示开启@PreAuthroize注解,是方法或类级别的注解,只需要在方法上添加@PreAuthroize注解即可,可以在资源被访问之前判断用户权限,是比较常用的方法,比如

图中注解就表示只有具备url1权限的用户才能执行方法queryName()。

第二个配置项securedEnable是专门用于判断是否具有角色的,可以在方法或者类上使用,参数需要以ROLE_开头,本文未使用所以忽略

2,UserDetailsServiceImpl

该方法实现了框架的UserDetailsService接口,并复写了loadUserByUsername方法,用于查询数据库的用户信息,添加权限控制功能后,需要同时查询用户的权限信息,代码如下:

package com.sgp.ss.security;import com.sgp.ss.dao.IUserMapper;
import com.sgp.ss.domain.entity.PermissionEntity;
import com.sgp.ss.domain.entity.UserEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;/*** @author shanguangpu* @date 2023/11/30 15:50*/
@Component
public class UserDetailsServiceImpl implements UserDetailsService {@AutowiredIUserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {try {UserEntity userEntityByName = userMapper.getUserEntityByName(username);if (userEntityByName == null){throw new UsernameNotFoundException("用户"+username+"不存在");}List<PermissionEntity> permissionList = userMapper.findPermissionByUsername(username);//查询用户权限信息List<GrantedAuthority> grantedAuthorities=new ArrayList<>();for (PermissionEntity p : permissionList){grantedAuthorities.add(new SimpleGrantedAuthority(p.getPermission()));}return new LoginUser(userEntityByName,grantedAuthorities);//测试用
//            List<String> list = new ArrayList<>(Arrays.asList("test"));
//            List<GrantedAuthority> grantedAuthorities=new ArrayList<>();
//            for(String s:list){
//                grantedAuthorities.add(new SimpleGrantedAuthority(s));
//            }
//            return new LoginUser(userEntityByName,list);} catch (Exception e){e.printStackTrace();}return null;}
}

其中方法findPermissionByUsername()是我们新加的,需要在mapper文件中编写SQL,实现根据用户名查询用户权限的功能,这个SQL语句可以按照下述格式直接写,如果数据库表名称不同,只需要修改表名就行

<select id="findPermissionByUsername" resultType="com.sgp.ss.domain.entity.PermissionEntity" parameterType="java.lang.String"><if test="_parameter != null">SELECT DISTINCT permission.id,permission.name,permission.permission FROMuserLEFT JOIN user_role on user.id = user_role.user_idLEFT JOIN role on user_role.role_id = role.idLEFT JOIN role_permission on role.id = role_permission.role_idLEFT JOIN permission on role_permission.permission_id = permission.idwhere user.username = #{username} and 1 = 1<!--select <include refid="UserEntityColumns"/> from user--><!--where username = #{username} and 1 = 1--></if></select>
3,LoginUser 

从上述代码中可以看出,当我们从数据库查询完用户权限信息后,框架会将它封装到用户对象UserDetails中,代码如下:

package com.sgp.ss.security;import com.sgp.ss.domain.entity.UserEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.*;
import java.util.stream.Collectors;/*** @author shanguangpu* @date 2023/11/30 15:58*/
public class LoginUser extends UserEntity implements UserDetails {Collection<? extends GrantedAuthority> authorities;private List<String> permissions = new ArrayList<>();public LoginUser(UserEntity userEntity){if (null != userEntity){this.setUsername(userEntity.getUsername());this.setPassword(userEntity.getPassword());}}public LoginUser(UserEntity userEntity, Collection<? extends GrantedAuthority> authorities){this.setId(userEntity.getId());this.setUsername(userEntity.getUsername());this.setPassword(userEntity.getPassword());
//        permissions.add(userEntity.getPermission());this.authorities = authorities;}public LoginUser(UserEntity userEntity, List<String> permissions){this.setId(userEntity.getId());this.setUsername(userEntity.getUsername());this.setPassword(userEntity.getPassword());this.permissions = permissions;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {if (authorities != null){return authorities;}authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());return authorities;}/*** 账户是否过期* @return*/@Overridepublic boolean isAccountNonExpired() {return true;}/*** 账户是否被锁定* @return*/@Overridepublic boolean isAccountNonLocked() {return true;}/*** 证书是否过期* @return*/@Overridepublic boolean isCredentialsNonExpired() {return true;}/*** 账户是否有效* @return*/@Overridepublic boolean isEnabled() {return true;}
}
4,JwtAuthenticationTokenFilter

下次访问资源只需要从token中解析出权限信息即可,所以需要在JWT过滤器中添加token解析步骤,并交给框架进行权限比对,存在上下文中

package com.sgp.ss.security;import com.sgp.ss.domain.entity.UserEntity;
import com.sgp.ss.service.UserEntityService;
import com.sgp.ss.util.JwtTokenUtil;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;/*** @author shanguangpu* @date 2023/12/8 15:29*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Autowiredprivate UserEntityService userEntityService;@Autowiredprivate JwtTokenUtil jwtTokenUtil;//    @Autowired
//    private JwtProperties jwtProperties;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {//获取token信息final String authorization = request.getHeader("token");String name = null;String authToken = null;if (!StringUtils.isEmpty(authorization)) {authToken = authorization.replace("Authorization", "");try {name = jwtTokenUtil.getUsernameFromToken(authToken);} catch (ExpiredJwtException e){e.printStackTrace();}}if (name != null && SecurityContextHolder.getContext().getAuthentication() == null){
//            if (jwtTokenUtil.isTokenValid(name, authToken)){UserEntity userEntityByName = userEntityService.getUserEntityByName(name);List<GrantedAuthority> authorityFromToken = jwtTokenUtil.getAuthorityFromToken(authToken);UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userEntityByName, null, authorityFromToken);SecurityContextHolder.getContext().setAuthentication(authentication);
//            }}filterChain.doFilter(request, response);}public JwtAuthenticationTokenFilter(){super();}
}
5,JWT工具类

然后在JWT工具类中实现权限获取方法,完整代码如下:

package com.sgp.ss.util;import com.sgp.ss.security.LoginUser;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.DefaultClock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;/*** @author shanguangpu* @date 2023/2/22 10:42*/
@Component
public class JwtTokenUtil implements Serializable {private static final Logger LOG = LoggerFactory.getLogger(JwtTokenUtil.class);private static final long serialVersionUID = -1264536648286114018L;private Clock clock = DefaultClock.INSTANCE;private Map<String, String> tokenMap = new ConcurrentHashMap<>(32);public String generateToken(LoginUser loginUser){Map<String, Object> claims = new HashMap<>();claims.put("username", loginUser.getUsername());claims.put("authority", loginUser.getAuthorities());String token = generateToken(claims, loginUser.getId() + "");if (! StringUtils.isEmpty(token)){}return token;}/*** 生成token* @param claims* @param subject* @return*/private String generateToken(Map<String, Object> claims, String subject) {final Date createdDate = clock.now();final Date expirationDate = generateExpirationDate(createdDate);return Jwts.builder()// 自定义属性.setClaims(claims).setSubject(subject)// 创建时间.setIssuedAt(createdDate)// 过期时间.setExpiration(expirationDate)// 签名算法及秘钥.signWith(SignatureAlgorithm.HS512, "sgpsgp").compact();}/*** 设置过期时间,以当前时间加上毫秒数计算,该方法中设置的过期时间是6小时* @param createdDate* @return*/private Date generateExpirationDate(Date createdDate) {return new Date(createdDate.getTime() + 21600 * 1000);}// 从得到的令牌里面获取用户IDpublic Integer getUserIdFromToken(String token) {Integer id = null;try {final Claims claims = getClaimsFromToken(token);id = Integer.parseInt(claims.getSubject());return id;} catch (Exception e) {}return id;}// 从得到的令牌里面获取用户名public String getUsernameFromToken(String token) {String username;try {final Claims claims = getClaimsFromToken(token);username = (String) claims.get("username");} catch (Exception e) {username = null;}return username;}// 从得到的令牌里面获取权限public List<GrantedAuthority> getAuthorityFromToken(String token) {List<GrantedAuthority> list = new ArrayList<>();try {final Claims claims = getClaimsFromToken(token);StringBuffer sb = new StringBuffer();List<LinkedHashMap> authority2 = (List<LinkedHashMap>) claims.get("authority");for (LinkedHashMap s : authority2){String authority = (String) s.get("authority");sb.append(authority+",");list.add(new SimpleGrantedAuthority(authority));}LOG.info("当前用户名称是:{},权限url为:{}",claims.get("username"),sb.subSequence(0,sb.length()-1));return list;} catch (Exception e) {e.printStackTrace();}return list;}// 从得到的令牌里面获取创建时间public Date getCreatedDateFromToken(String token) {Date created;try {final Claims claims = getClaimsFromToken(token);created = claims.getIssuedAt();} catch (Exception e) {created = null;}return created;}// 从得到的令牌里面获取过期时间public Date getExpirationDateFromToken(String token) {Date expiration;try {final Claims claims = getClaimsFromToken(token);expiration = claims.getExpiration();} catch (Exception e) {expiration = null;}return expiration;}private Claims getClaimsFromToken(String token) {Claims claims;try {claims = Jwts.parser().setSigningKey("sgpsgp").parseClaimsJws(token).getBody();} catch (Exception e) {claims = null;}return claims;}/*** 检查token是否过期* @param token* @return*/public Boolean isTokenExpired(String token) {final Date expiration = getExpirationDateFromToken(token);boolean flag = expiration.before(new Date());if(flag) {String username = getUsernameFromToken(token);if(username != null) {tokenMap.remove(username);}}return flag;}public Boolean isTokenValid(String username, String token) {if(tokenMap.get(username) != null && tokenMap.get(username).equals(token.trim())) {return !isTokenExpired(token);}return false;}public Boolean isTokenValid(int id, String token) {if (tokenMap.get(id) != null && tokenMap.get(id).equals(token.trim())) {return !isTokenExpired(token);}return false;}public void deleteToken(String token) {String username = getUsernameFromToken(token);if(username != null) {tokenMap.remove(username);}}}}
6,controller

controller文件中可以多写几个方法,分别给与不同的权限,代码如下:

package com.sgp.ss.controller;import com.sgp.ss.dao.*;
import com.sgp.ss.domain.dto.data.*;
import com.sgp.ss.domain.entity.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @author shanguangpu* @date 2023/12/12 14:59*/
@RestController
public class index {@Autowiredprivate IUserMapper userMapper;@Autowiredprivate IRoleMapper roleMapper;@Autowiredprivate IPermissionMapper permissionMapper;@Autowiredprivate IUserRoleMapper userRoleMapper;@Autowiredprivate IRolePermissionMapper rolePermissionMapper;@GetMapping(value = "hello")public String request(){System.out.println("hello word");return "hello success";}@PostMapping(value = "select")@PreAuthorize("hasAuthority('url2')")public String selectInfo(){List<UserEntity> userEntities = userMapper.queryUserEntityList(new UserDto());return userEntities.toString();}@PostMapping(value = "queryName")@PreAuthorize("hasAuthority('url1')")public String queryName(){List<UserEntity> userEntities = userMapper.queryUserEntityList(new UserDto());int i = 1;for (UserEntity userEntity : userEntities){String username = userEntity.getUsername();System.out.println("name"+i+" is : "+username);i++;}return "query finish";}@PostMapping(value = "queryTable")public String queryRoleAndPermission(){List<RoleEntity> roleEntities = roleMapper.queryRoleEntityList(new RoleDto());for (RoleEntity roleEntity : roleEntities){System.out.println(roleEntity.getId()+","+roleEntity.getName()+","+roleEntity.getDescription());}List<PermissionEntity> permissionEntities = permissionMapper.queryPermissionEntityList(new PermissionDto());for (PermissionEntity permissionEntity : permissionEntities){System.out.println(permissionEntity.getId()+","+permissionEntity.getName()+","+permissionEntity.getPermission());}List<RolePermissionEntity> rolePermissionEntities = rolePermissionMapper.queryRolePermissionEntityList(new RolePermissionDto());for (RolePermissionEntity rolePermissionEntity : rolePermissionEntities){System.out.println(rolePermissionEntity.getId()+","+rolePermissionEntity.getPermission_id()+","+rolePermissionEntity.getRole_id());}List<UserRoleEntity> userRoleEntities = userRoleMapper.queryUserRoleEntityList(new UserRoleDto());for (UserRoleEntity userRoleEntity: userRoleEntities){System.out.println(userRoleEntity.getId()+","+userRoleEntity.getRole_id()+","+userRoleEntity.getUser_id());}List<PermissionEntity> bbb = userMapper.findPermissionByUsername("bbb");for (PermissionEntity p : bbb){System.out.println(p.getId()+","+p.getName()+","+p.getPermission());}return "success";}}
postman测试
1,获取token

首先我们通过用户名获取到token

2,具备权限测试

然后携带这个token,访问接口/queryTable,这个接口可以查询出数据库里所有相关的数据,以及用户bbb的权限列表打印在IDEA控制台

可以看到,用户bbb具备访问url1和url3的权限,那么我们继续测试接口/queryName,这个接口上方注解标注了需要具备url1权限才能访问,如下

3,不具备权限测试

然后测试接口/select,该接口上方注解表示只有具备url2的权限才能访问,用户bbb不具备这个权限,所以访问会失败,如下

至此,权限控制功能测试完毕

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/523280.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于云上分布式NoSQL的海量气象数据存储和查询方案

摘要&#xff1a; 气象数据是一类典型的大数据&#xff0c;具有数据量大、时效性高、数据种类丰富等特点&#xff0c;每天产生的数据量常在几十TB到上百TB的规模&#xff0c;且在爆发性增长。如何存储和高效的查询这些气象数据越来越成为一个难题&#xff0c;本文针对气象领域中…

现代IM系统中消息推送和存储架构的实现

摘要&#xff1a; 前言 IM全称是『Instant Messaging』&#xff0c;中文名是即时通讯。在这个高度信息化的移动互联网时代&#xff0c;生活中IM类产品已经成为必备品&#xff0c;比较有名的如钉钉、微信、QQ等以IM为核心功能的产品。当然目前微信已经成长为一个生态型产品&…

漫画:为什么程序员没有女友?

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 程序员小灰————— 五分钟后 —————ERNIE是百度开源深度学习平台飞桨&#xff08;PaddlePaddle&#xff09;推出的知识增强语义表示模型&#xff0c;通过海量数据建模词、实体及实体关系。相较于 BERT 学习原始语言信…

基于TableStore的数据采集分析系统介绍

摘要&#xff1a; 摘要 在互联网高度发达的今天&#xff0c;ipad、手机等智能终端设备随处可见&#xff0c;运行在其中的APP、网站也非常多&#xff0c;如何采集终端数据进行分析&#xff0c;提升软件的品质非常重要&#xff0c;例如PV/UV统计、用户行为数据统计与分析等。虽然…

idea 暂存文件或idea切换分支代码不见了

idea切换分支时&#xff0c;修改过的代码文件全部不见了 找了一下问题&#xff0c;切换分支时&#xff0c;idea自动会创建暂存文件&#xff0c; 点开&#xff0c;右边View --> 即可显示暂存文件。 点击Apply Stash 即可将暂存文件应用到当前分支。 如果发现此方法还是不行…

微服务架构之「 访问安全 」

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 奎哥来源 | 不止思考应用程序的访问安全又是我们每一个研发团队都必须关注的重点问题。尤其是在我们采用了微服务架构之后&#xff0c;项目的复杂度提升了N个级别&#xff0c;相应的&#xff0c;微服务的安全工作也就更难更复…

百亿级全网舆情分析系统存储设计

摘要&#xff1a; 前言 在时下互联网信息的浪潮下&#xff0c;信息的传播速度远超我们的想象。微博里一条大V的帖子&#xff0c;朋友圈的一个状态更新&#xff0c;热门论坛的一条新闻&#xff0c;购物平台的购物评价&#xff0c;可能会产生数以万计的转发&#xff0c;关注&…

面试官:你简历中写用过docker,能说说容器和镜像的区别吗?

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | bethal来源 | http://sina.lt/gfmf这篇文章希望能够帮助读者深入理解Docker的命令&#xff0c;还有容器&#xff08;container&#xff09;和镜像&#xff08;image&#xff09;之间的区别&#xff0c;并深入探讨容器和运行中…

IPv6终于要取代IPv4了!阿里云将全面提供IPv6服务

摘要&#xff1a; 近日&#xff0c;中办国办印发《推进互联网协议第六版&#xff08;IPv6&#xff09;规模部署行动计划》&#xff0c;加快推进基于IPv6的下一代互联网规模部署&#xff0c;计划指出到2018年末国内IPv6活跃用户数要达到2亿&#xff0c;2020年末达到5亿&#xff…

SDN精华问答 | 为什么会出现SDN?

SDN火热了好一阵子&#xff0c;无论运营商、政府企业、投资机构&#xff0c;一段时间&#xff0c;不知道SDN、不能甩几个SDN相关的名词术语&#xff0c;似乎都落后于时代了。今天&#xff0c;就来看看关于SDN的精华问答吧。1Q&#xff1a;SDN的本质属性&#xff1f; A&#xff…

一张图看懂2017双11中的网络产品和技术

摘要&#xff1a; 大家都知道&#xff0c;2017年双11又创造了新纪录&#xff0c;全天交易额1682亿&#xff0c;交易峰值32.5万笔/秒&#xff0c;支付峰值25.6W笔/秒&#xff0c;狂欢的背后是极其复杂庞大的技术系统&#xff0c;其中就有大量阿里云云计算相关的产品和技术&#…

开源Elasticsearch云托管服务,专享企业级服务

摘要&#xff1a; 日前&#xff0c;阿里云联合开源官方Elastic联合发布了Elasticsearch新产品。该产品基于开源Elasticsearch及商业版X-Pack插件的云托管服务&#xff0c;保证高性能、高可用&#xff0c;可弹性扩容&#xff0c;并提供企业级权限管控、安全监控告警等高级功能,适…

Ivanti罗琦:IT服务管理中“拧紧螺丝”要有门道儿!

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 刘晶晶Ivanti与科大讯飞的携手合作&#xff01;初听这一消息&#xff0c;阿晶惊讶不已。毕竟科大讯飞擅长语音众所周知&#xff0c;Ivanti更专攻IT服务管理&#xff0c;看似“不相关”的两家企业&#xff0c;却有了某种隐含的…

CDN价格下调25% 阿里云再次冲击国内最低价

摘要&#xff1a; 在刚刚圆满落幕的云栖大会广东分会上&#xff0c;阿里云发布了很多重磅信息&#xff0c;其中不乏很多核心产品折扣幅度调整的信息。令人惊喜的是&#xff0c;阿里云CDN价格再次下调&#xff0c;降幅高达25%。降价完成后&#xff0c;带宽单价最低只需0.54元/Mb…

SpringBoot整合Spring Cloud Alibaba

spring-cloud官方中文文档https://www.springcloud.cc/spring-cloud-dalston.htmlspring-cloud官方文档https://cloud.spring.io/spring-cloud-static/Greenwich.SR1/single/spring-cloud.html#_quick_start_6Spring Cloud Alibaba官方文档https://github.com/alibaba/spring-c…

产品经理教你玩转阿里云负载均衡SLB系列(一):快速入门--什么是负载均衡

摘要&#xff1a; 负载均衡是一种技术&#xff0c;从字面意义上理解&#xff0c;就是让负载(变得)均衡&#xff0c;负载是什么呢&#xff1f;可以理解为工作量、工作强度。用日常生活中的例子来打比方&#xff0c;一群建筑工人&#xff0c;盖一幢楼房&#xff0c;有搬砖的&…

android touch事件坐标原点,Android onTouch事件与手势操作

触摸&#xff0c;手势操作已经很好的融入了我们的生活。那么Android开发中触摸事件要如何捕捉&#xff1f;如何处理&#xff1f;如何识别手势&#xff1f;事件的传递机制又是怎么样的&#xff1f;下面我们将通过一个小例子来进行这方面的学习。先看效果图如上图所示&#xff0c…

2019年技术盘点容器篇(二):听腾讯云讲讲踏入成熟期的容器技术 | 程序员硬核评测...

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者&#xff1a;刘晶晶据相关调研机构出具的报告数据显示&#xff0c;目前应用容器市场规模将从2016年的 7.62亿美元增长到2020年的27亿美元。显而易见&#xff0c;引入容器所展现的巨大灵活性有效推动了其采用速率&#xff0c;使企…

阿里云容器服务新增支持Kubernetes编排系统,性能重大提升

摘要&#xff1a; 作为容器编排系统的两大流派&#xff0c; Kubernetes和Swarm的重要性不言而喻。融合了两大高性能集成的阿里云容器服务&#xff0c;不仅可以降低50%的基础架构成本&#xff0c;提高交付速度将产品迭代加快13倍&#xff0c;还可以实现秒级的海量容器启动、秒级…

实战_01_Spring SpringMVC 整合Mybaits

文章目录一、技术选型1.1. maven坐标说明1.2. 环境准备二、工程所属关系2.1. 项目结构总览2.2. 工程所属关系总览三、创建聚合工程3.1. 聚合工程_ly-parent3.2. 聚合工程_ly-manager四、创建子项目4.1 创建子工程服务ly-common4.2 创建子工程服务ly-entity4.3 创建子工程服务ly…