微服务(四)—— 用户模块(backend-user)

08-微服务版单点登陆系统(SSO)实践(2107~2108~2109~2110) 分享一些资料给大家,我觉得这些都是很有用的东西,大家也可以跟着来学习,查漏补缺。《Java高级面试》《Java高级架构知识》《算法知识》合法性)。*//**配置令牌的存储策略,对于oauth2规范中提供了这样的几种策略1)JdbcTokenStore(这里是要将token存储到关系型数据库)2)RedisTokenStore(这是要将token存储到redis数据库-key/value)3)JwtTokenStore(这里是将产生的token信息存储客户端,并且token。 阅读详情

上一篇:微服务(三)—— MyBatis逆向工程模块(backend-generator).

一、创建项目

首先创建一个SpringBoot和Spring Cloud项目,这里需要考虑SpringBoot和Spring Cloud的版本问题
在这里插入图片描述
这是官网给的Spring Cloud对应的Spring Boot版本
在这里插入图片描述
在BackendManageSystem模块new 一个 module
在这里插入图片描述
选择Spring Initializr,点击Next
选择版本
在这里插入图片描述
选择Spring Boot版本,Idea里都是最新版,我使用的是2.2.2版本,创建后修改Maven依赖即可;Lombok用于加构造方法注解;
然后是Web,JDBC、Mybatis相关的依赖,因为用户系统是需要往注册中心注册的,注册中心使用的是Spring Cloud的Eureka组件,所以需要Eureka Discover Client依赖;
在这里插入图片描述
创建项目名称
在这里插入图片描述

二、项目结构

1.目录结构

在这里插入图片描述
controller用来接收前端的请求,下面看UserController的具体细节

package com.aiun.user.controller;
/**
 * 用户模块控制层
 * @author lenovo
 */
@Api(tags = "用户系统相关接口")
@RestController
@RequestMapping("/user/")
public class UserController {
    @Autowired
    private IUserService iUserService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 用户登录
     * @param username 用户名
     * @param password 密码
     * @return 返回登录信息
     */
    @PostMapping("login")
    @ApiOperation(value = "用户登录", notes = "用户登录")
    public ServerResponse<User> login(String username, String password) {
        ServerResponse<User> response = iUserService.login(username, password);
        return response;
    }

    /**
     * 用户注册
     * @param user 用户信息
     * @return 返回注册信息
     */
    @PostMapping("register")
    @ApiOperation(value = "用户注册")
    public ServerResponse<String> register(User user) {
        return iUserService.register(user);
    }

    /**
     * 退出登录
     * @param request 请求
     * @return 返回退出状态
     */
    @PostMapping("logout")
    @ApiOperation(value = "用户登录退出")
    public ServerResponse<String> logout(HttpServletRequest request) {
        String key = request.getHeader(UserConst.AUTHORITY);
        ValueOperations<String, String> operations = redisTemplate.opsForValue();
        String value = operations.get(key);
        User user = JsonUtils.jsonStr2Object(value, User.class);
        if (key.equals(user.getUsername())) {
            redisTemplate.delete(key);
            return ServerResponse.createBySuccessMessage("退出成功");
        }
        return ServerResponse.createBySuccessMessage("当前用户未登录");
    }

    /**
     * 验证信息
     * @param str 校验的数据
     * @param type 类型(userName, email)
     * @return 返回校验结果
     */
    @PostMapping("check_valid")
    @ApiOperation(value = "验证信息")
    public ServerResponse<String> checkValid(String str, String type){
        return iUserService.checkValid(str, type);
    }

    /**
     * 获取用户信息
     * @param request 请求
     * @return 返回用户信息
     */
    @PostMapping("get_information")
    @ApiOperation(value = "获取用户信息")
    public ServerResponse<User> getInformation(HttpServletRequest request) {
        ServerResponse hasLogin = loginHasExpired(request);
        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iUserService.getInformation(user.getId());
        }

        return hasLogin;
    }

    /**
     * 判断用户登录是否过期
     */
    private ServerResponse<User> loginHasExpired(HttpServletRequest request) {
        String key = request.getHeader(UserConst.AUTHORITY);
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        String value = valueOperations.get(key);
        if (StringUtils.isEmpty(value)) {
            return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
        }
        User user = JsonUtils.jsonStr2Object(value, User.class);
        if (!key.equals(user.getUsername())) {
            return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
        }
        valueOperations.set(key, value, 1, TimeUnit.HOURS);
        return ServerResponse.createBySuccess(user);
    }
}

这里涉及到接口文档描述Swagger和微服务单点登录问题,看后面介绍。
微服务(十二)—— Swagger.
微服务(十)—— 单点登录.
controller层调用service层接口,下面看service层的实现类UserServiceImpl

package com.aiun.user.service.impl;

/**
 * 用户模块实现类
 * @author lenovo
 */
@Service("iUserService")
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Override
    public ServerResponse<User> login(String userName, String password) {
        int resultCount = userMapper.checkUsername(userName);
        if (resultCount <= 0) {
            return ServerResponse.createByErrorMessage("用户名不存在");
        }
        //密码登录并MD5加密
        String md5password = MD5Utils.MD5EncodeUtf8(password);
        User user = userMapper.selectLogin(userName, md5password);
        if (user == null) {
            return ServerResponse.createByErrorMessage("密码错误");
        }
        String key = userName;
        String token = JsonUtils.object2JsonStr(user);
        ValueOperations<String, String>valueOperations = redisTemplate.opsForValue();
        //用户登录成功后,将该用户对象作为token值保存到redis里,并且设置过期时间为1小时
        valueOperations.set(key, token, 1, TimeUnit.HOURS);
        return ServerResponse.createBySuccess("登录成功", user);
    }

    @Override
    public ServerResponse<String> register(User user) {
        ServerResponse validResponse = this.checkValid(user.getUsername(), UserConst.USERNAME);
        if (!validResponse.isSuccess()) {
            return validResponse;
        }
        validResponse = this.checkValid(user.getEmail(), UserConst.EMAIL);
        if (!validResponse.isSuccess()) {
            return validResponse;
        }

        // 指定用户角色
        user.setRole(UserConst.Role.ROLE_CUSTOMER);
        // MD5 加密
        user.setPassword(MD5Utils.MD5EncodeUtf8(user.getPassword()));

        int resultCount = userMapper.insert(user);
        if(resultCount == 0) {
            return ServerResponse.createByErrorMessage("注册失败");
        }

        return ServerResponse.createBySuccessMessage("注册成功");
    }

    @Override
    public ServerResponse<User> getInformation(int userId) {
        User user = userMapper.selectByPrimaryKey(userId);
        if(user == null) {
            return ServerResponse.createByErrorMessage("当前用户不存在");
        }
        user.setPassword(StringUtils.EMPTY);

        return ServerResponse.createBySuccess(user);
    }
    //.......
}

用户接口层IUserService

package com.aiun.user.service;

/**
 * 用户接口
 * @author lenovo
 */
public interface IUserService {
    /**
     * 用户登录
     * @param userName
     * @param password
     * @return
     */
    ServerResponse<User> login(String userName, String password);
    /**
     * 注册
     * @param user
     * @return
     */
    ServerResponse<String> register(User user);

    /**
     * 用户名或邮箱的校验
     * @param str
     * @param type
     * @return
     */
    ServerResponse<String> checkValid(String str, String type);

    /**
     * 获取用户信息
     * @param userId
     * @return
     */
    ServerResponse<User> getInformation(int userId);

    /**
     * 更改密码
     * @param oldPassword
     * @param newPassword
     * @param user
     * @return
     */
    ServerResponse<String> resetPassword(String oldPassword, String newPassword, User user);
}

Server层的实现类调用DAO层的Mapper层,UserMapper如下:

package com.aiun.user.mapper;

@Mapper
@Repository
public interface UserMapper {
    /**
     * 根据主键删除用户
     * @param id 主键
     * @return 影响的记录行数
     */
    int deleteByPrimaryKey(Integer id);

    /**
     * 插入用户
     * @param record 用户信息
     * @return 影响的记录行数
     */
    int insert(@Param("record") User record);

    /**
     * 有选择的插入用户
     * @param record 用户信息
     * @return 影响的记录行数
     */
    int insertSelective(@Param("record") User record);

    /**
     * 通过主键查询用户
     * @param id 主键
     * @return 用户信息
     */
    User selectByPrimaryKey(Integer id);

    /**
     * 通过主键有选择的更新用户
     * @param record 用户信息
     * @return 影响的记录行数
     */
    int updateByPrimaryKeySelective(@Param("record") User record);

    /**
     * 通过主键更新用户
     * @param record 用户信息
     * @return 影响的记录行数
     */
    int updateByPrimaryKey(@Param("record") User record);

    /**
     * 登录
     * @param username 用户名
     * @param password 密码
     * @return 返回用户信息
     */
    User selectLogin(@Param("username") String username, @Param("password") String password);

    /**
     * 检查用户名
     * @param username 用户名
     * @return 影响的记录行数
     */
    int checkUsername(String username);

    /**
     * 检查email
     * @param email 邮箱
     * @return 影响的记录行数
     */
    int checkEmail(String email);

    /**
     * 检查密码
     * @param password
     * @param userId
     * @return
     */
    int checkPassword(@Param("password") String password, @Param("userId") Integer userId);
}

注:这个Mapper层的接口除了要写Spring的@Repository注解,还需要写@Mapper注解,这个注解是告诉SpringBoot这是DAO层的Mapper映射接口,与这个接口对应的是mapper.xml文件,注意是SQL语句的编写,如下UserMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.aiun.user.mapper.UserMapper" >
  <resultMap id="BaseResultMap" type="com.aiun.user.pojo.User" >
    <constructor >
      <idArg column="id" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="username" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="password" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="email" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="phone" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="question" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="answer" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="role" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="create_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
      <arg column="update_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
    </constructor>
  </resultMap>
  <sql id="Base_Column_List" >
    id, username, password, email, phone, question, answer, role, create_time, update_time
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select
    <include refid="Base_Column_List" />
    from trade_user
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from trade_user
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.aiun.user.pojo.User" >
    insert into trade_user (id, username, password,
      email, phone, question,
      answer, role, create_time,
      update_time)
    values (#{record.id,jdbcType=INTEGER}, #{record.username,jdbcType=VARCHAR}, #{record.password,jdbcType=VARCHAR},
      #{record.email,jdbcType=VARCHAR}, #{record.phone,jdbcType=VARCHAR}, #{record.question,jdbcType=VARCHAR},
      #{record.answer,jdbcType=VARCHAR}, #{record.role,jdbcType=INTEGER}, now(),
      now())
  </insert>
  <insert id="insertSelective" parameterType="com.aiun.user.pojo.User" >
    insert into trade_user
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        id,
      </if>
      <if test="username != null" >
        username,
      </if>
      <if test="password != null" >
        password,
      </if>
      <if test="email != null" >
        email,
      </if>
      <if test="phone != null" >
        phone,
      </if>
      <if test="question != null" >
        question,
      </if>
      <if test="answer != null" >
        answer,
      </if>
      <if test="role != null" >
        role,
      </if>
      <if test="createTime != null" >
        create_time,
      </if>
      <if test="updateTime != null" >
        update_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        #{record.id,jdbcType=INTEGER},
      </if>
      <if test="username != null" >
        #{record.username,jdbcType=VARCHAR},
      </if>
      <if test="password != null" >
        #{record.password,jdbcType=VARCHAR},
      </if>
      <if test="email != null" >
        #{record.email,jdbcType=VARCHAR},
      </if>
      <if test="phone != null" >
        #{record.phone,jdbcType=VARCHAR},
      </if>
      <if test="question != null" >
        #{record.question,jdbcType=VARCHAR},
      </if>
      <if test="answer != null" >
        #{record.answer,jdbcType=VARCHAR},
      </if>
      <if test="role != null" >
        #{record.role,jdbcType=INTEGER},
      </if>
      <if test="createTime != null" >
        #{record.createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="updateTime != null" >
        now(),
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.aiun.user.pojo.User" >
    update trade_user
    <set >
      <if test="record.username != null" >
        username = #{record.username,jdbcType=VARCHAR},
      </if>
      <if test="record.password != null" >
        password = #{record.password,jdbcType=VARCHAR},
      </if>
      <if test="record.email != null" >
        email = #{record.email,jdbcType=VARCHAR},
      </if>
      <if test="record.phone != null" >
        phone = #{record.phone,jdbcType=VARCHAR},
      </if>
      <if test="record.question != null" >
        question = #{record.question,jdbcType=VARCHAR},
      </if>
      <if test="record.answer != null" >
        answer = #{record.answer,jdbcType=VARCHAR},
      </if>
      <if test="record.role != null" >
        role = #{record.role,jdbcType=INTEGER},
      </if>
      <if test="record.createTime != null" >
        create_time = #{record.createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.updateTime != null" >
        update_time = now(),
      </if>
    </set>
    where id = #{record.id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.aiun.user.pojo.User" >
    update trade_user
    set username = #{record.username,jdbcType=VARCHAR},
      password = #{record.password,jdbcType=VARCHAR},
      email = #{record.email,jdbcType=VARCHAR},
      phone = #{record.phone,jdbcType=VARCHAR},
      question = #{record.question,jdbcType=VARCHAR},
      answer = #{record.answer,jdbcType=VARCHAR},
      role = #{record.role,jdbcType=INTEGER},
      create_time = #{record.createTime,jdbcType=TIMESTAMP},
      update_time = now()
    where id = #{id,jdbcType=INTEGER}
  </update>
  <select id="checkUsername" resultType="int" parameterType="string">
    select count(1) from trade_user
    where username = #{username}
  </select>
  <select id="selectLogin" resultMap="BaseResultMap" parameterType="map">
    select
    <include refid="Base_Column_List" />
    from trade_user
    where username = #{username}
    and password = #{password}
  </select>
  <select id="checkEmail" resultType="int" parameterType="string">
    select count(1) from trade_user
    where email = #{email}
  </select>
  <select id="checkPassword" resultType="int" parameterType="map">
    select count(1) from trade_user
    where id = #{userId}
    and password = #{password}
  </select>
</mapper>

对于返回User对象的我用BaseResultMap进行映射和封装。
数据库建表语句:

CREATE TABLE reade_user (
	id int(11) NOT NULL AUTO_INCREMENT COMMENT ‘用户表id’,
	username varchar(50) NOT NULL COMMENT ‘用户名’,
	password varchar(50) NOT NULL COMMENT ‘用户密码,MD5加密’,
	email varchar(50) DEFAULT NULL,
	phone varchar(20) DEFAULT NULL,
	question varchar(100) DEFAULT NULL COMMENT ‘找回密码问题’,
	answer varchar(100) DEFAULT NULL COMMENT ‘找回密码答案’,
	role int(4) NOT NULL COMMENT ‘角色0-管理员,1-普通用户’,
	create_time datetime NOT NULL COMMENT ‘创建时间’,
	update_time datetime NOT NULL COMMENT ‘最后一次更新时间’,
	PRIMARY KEY (id),
	UNIQUE KEY user_name_unique (username) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;

2.pom.xml

首先引入父模块依赖

<parent>
    <groupId>com.aiun</groupId>
    <artifactId>BackendManageSystem</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>

引入backend common 依赖

<dependency>
    <groupId>com.aiun</groupId>
    <artifactId>backend-common</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

由于我在父模块添加了Spring Cloud依赖,所以这里就不需要添加Spring Cloud依赖,父模块的pom.xml文件见微服务(一)—— 项目结构.
pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.aiun</groupId>
        <artifactId>BackendManageSystem</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.aiun</groupId>
    <artifactId>backend-user</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>backend-user</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--backend common 依赖-->
        <dependency>
            <groupId>com.aiun</groupId>
            <artifactId>backend-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--config client 依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-client</artifactId>
        </dependency>
        <!--spring boot test 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--spring boot web 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mybatis 依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!--mysql 依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--druid 连接池依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <!-- lombok 依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- swagger 依赖-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
</project>

3.配置文件application.yml

# Spring
spring:
 # 服务应用名称
 application:
  name: backend-user
  # 数据源
 datasource:
  driver-class-name: com.mysql.cj.jdbc.Driver
  url: jdbc:mysql://localhost:3306/trade?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
  username: root
  password: root
  # 指定druid 连接池以及druid 连接池配置
  type: com.alibaba.druid.pool.DruidDataSource
  druid:
   initial-size: 1          # 初始连接数
   max-active: 20           # 最大连接数
   max-idle: 20             # 最大空闲
   min-idle: 1              # 最小空闲
   max-wait: 60000          # 最长等待时间

server:
 port: 8084                  # 项目访问端口
 servlet:
  context-path: /backend-user  # 项目访问路径

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
  instance:
    hostname: localhost

# MyBatis
mybatis:
 # 配置 MyBatis 数据返回类型别名(默认别名是类名)
 type-aliases-package: com.aiun.user.pojo
 # 配置 MyBatis Mapper 映射文件
 mapper-locations: classpath:/mappers/*.xml

# MyBatis SQL 打印(方法接口所在的包,不是 Mapper.xml 所在的包)
# logging:
 # level:
 # com.aiun.order.mappers: debug
 # pagehelper
 pagehelper:
   helperDialect: sqlite #postgresql
   reasonable: true
   supportMethodsArguments: true
   params: countSql
   count: countSql
   returnPageInfo: check

 # 记录日志
 logging:
   config: classpath:logback-spring.xml

该文件是SpringBoot的配置文件,与properties文件不同的是它是键: 值,并且冒号后面要有一个空格,包的子目录也是通过空格体现,而且一个文件可以配置几个模块,例如上面我配置了开发环境dev和测试环境test,通过—符号进行分割。
这个配置文件主要配置的是应用名称、访问路径、端口、数据库、数据库连接池、日志和其他框架的配置。

4.启动项目

打开SpringBoot的启动类UserApplication

package com.aiun.user;

/**
 * 用户模块启动类
 * @author lenovo
 */
@SpringBootApplication
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}

启动该项目即可,然后就可以通过访问http://localhost:8084/backend-user/user/访问项目了
我这里通过Google插件Talend API Test进行后端接口测试
在这里插入图片描述
成功!!!
到这里用户模块就完成了。
注:项目已上传GitHub:https://github.com/AiunCode/BackendManageSystem.

下一篇:微服务(五)—— 产品模块(backend-product).

基于OAuth2微服务的登录模块(总体介绍) 在系统比较庞大的时候,微服务便成了很多项目的最佳选择,因为他便与开发,便与维护,并且可以随意增加节点数量,提高并发量,提高系统的性能, 然而他的缺点也很多,1.服务多了,维护便是个问题,2.服务多了,链路多了,问题定位也是个问题 3.服务有很多,那么我们的性能分析也成了问题,那么多的服务,我怎么知道哪个服务性能慢呢 但是呢,以上的缺点都有解决方案,我们可以后面再说,接下来说的是登录模块,因为不管什么系统,登录是必须的,权限控制是必须的,既然是微服务,那么以前的那一套直接从数据库取用户信息验证密码的那一. 阅读详情

相关推荐

基于OAuth2微服务的登录模块(数据库服务build-data)

在这次的微服务设计中,我将数据库的操作集中到了一起,其他微服务获取数据都是通过feign调用获取,这样的好处是数据集中管理,便与数据处理和管控,因为只有一个数据库,所以也没有必要每个服务都配置相同的数据库配置,坏处就是每次调用都需要通过远程调用,增加了网络成本,以及我们的数据库访问接口都对外开放,这样也增加了风险(这个就需要授权来控制) 先看下工程结构: pom文件内容如下 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi

dw147258dw的专栏 432

微服务下实现单点登录(SSO)的几种方式

随着互联网的飞速发展和移动终端的广泛普及,互联网用户的体量越来越大,日活几亿的app层出不穷,无论是互联网服务还是app服务,对于服务器的高并发要求越来越高,服务器高性能、高可用越来越重要,所以分布式微服务架构开发的项目越来越多。我们往往会用多台服务器去部署不同的业务模块,比如电商项目中,可能购物车系统、订单系统、视频种草模块、秒杀系统等等都会在不同的服务器单独部署,微服务项目的优势主要体现在:1.高并发:很多人同时访问这个服务器,由于不同模块分布在不同服务器,单个服务器只需要运行单一功能模块。

Lee_92的博客 3412

通义千问AI模型对接飞书机器人-集成飞书机器人(2-2)

http请求地址上一篇百炼平台配置的应用地址。

hefaji的博客 1916

1.9 微服务版单点登陆系统(SSO)

1 单点登陆系统简介 1.1 背景分析 传统的登录系统中,每个站点都实现了自己的专用登录模块。各站点的登录状态相互不认可,各站点需要逐一手工登录。例如: 这样的系统,我们又称之为多点登陆系统。应用起来相对繁琐(每次访问资源服务都需要重新登陆认证和授权)。与此同时,系统代码的重复也比较高。由此单点登陆系统诞生。 1.2 单点登陆系统 单点登录,英文是 Single Sign On(缩写为 SSO)。即多个站点共用一台认证授权服务器,用户在其中任何一个站点登录后,可以免登录访问其他所有站点。而且,各站点间可以

蟑螂恶霸的博客 2391

别再纠结了!车规MCU选M7还是R52?一张图看懂你的车身控制器/域控制器该用谁

本文深入探讨车规MCU选型中Cortex-M7与Cortex-R52的核心差异,提供基于安全等级与实时性需求的选型决策图。通过对比ASIL认证、实时性能及成本效益,帮助工程师在车身控制器/域控制器设计中做出精准选择,避免潜在风险与隐性成本。

weixin_42628688的博客 397

Box2D-Lite源码阅读笔记(2)

0.初始化函数 static void InitDemo(int index) { world.Clear(); numBodies = 0; numJoints = 0; bomb = NULL; demoIndex = index; demos[index](bodies, joints); } 在这个初始化函数中,一般会默认执行index=0的函数,这个函数就是一个示例,因为示例比较简单,所以也便于研究,示例效果如下。 就是一个方块。 进入第一个示例函数。(忽略我加的注释)

ARTELE的博客 976

探索LiYupi's User-Center-Backend-Public: 一个高效且灵活的用户中心后台系统

探索LiYupi's User-Center-Backend-Public: 一个高效且灵活的用户中心后台系统 在今天的数字化世界中,构建稳定、安全并且功能强大的用户管理系统至关重要。 是这样一个开放源代码的项目,旨在为开发者提供一个高效且可定制的用户管理后台解决方案。 项目简介 User-Center-Backend-Public 是一款基于Java语言和Spring Boot框架开发的用户中心...

gitblog_00079的博客 471

XGBoost调参新姿势:Bayesian优化实战指南(附完整代码)

本文详细介绍了如何使用Bayesian优化方法高效调优XGBoost模型参数。通过解析Bayesian优化核心原理和XGBoost关键参数,提供完整的实战代码示例,帮助读者掌握这一智能调参技术,显著提升模型性能。文章特别适合机器学习工程师和数据科学家阅读。

weixin_42536058的博客 366

如何部署Microservices Event Sourcing?完整部署指南与环境配置

Microservices Event Sourcing是一个基于Spring Cloud的微服务架构在线购物平台,采用了事件溯源和CQRS架构模式,提供了构建端到端微服务的最佳实践。本文将为您提供完整的部署指南和环境配置步骤,帮助您快速搭建这个强大的微服务系统。 ## 🚀 系统架构概览 Microservices Event Sourcing采用三层架构设计,包含11个独立的微服务组件,确

gitblog_00529的博客 951

[Dify实战] 招投标标书智能生成:模板套用 + 风险校验

摘要:本文提出基于Dify构建AI标书生成工作流,解决传统标书撰写中内容重复(80%复用率)、人工修改效率低、风险检查易出错等痛点。方案包含三大核心模块:1)智能生成(基于模板自动填充项目信息);2)风险校验(AI自动检查废标风险点);3)多角色协作。通过拆分标书结构、设置详细Prompt、建立风险检查清单,实现从"人工撰写"到"参数化自动生成+AI审核"的转型,最终输出标准格式文档并支持企业系统集成。典型应用场景下,系统能自动生成技术方案并识别关键风险(如支付安全合

技术小甜甜:传播可落地技术,帮助个人和企业降低解决真实场景问题的成本 828

UG\NX二次开发 获取对象的层、颜色、隐藏状态、线宽、线型、高亮显示状态

获取对象的层、颜色、隐藏状态、线宽、线型、高亮显示状态。

王牌飞行员_里海的博客 1982

Microservices Event Sourcing用户指南:从OAuth2登录到完整购物流程

想要快速掌握基于事件溯源的微服务架构吗?Microservices Event Sourcing项目为你提供了一个完整的在线购物平台最佳实践!🚀 这个开源项目使用Spring Boot、Spring Cloud和OAuth2等技术,实现了从用户登录到完整购物流程的端到端解决方案。无论你是微服务新手还是经验丰富的开发者,本指南都将带你深入了解如何构建高可用的分布式系统。 ## 📋 项目架构概览

gitblog_02766的博客 431

rv1126如何切换720p和1080p

切换720p和1080p可以使用modetest,但是需要将这两种模式都添加到connector中去 添加一个新的mode到connector中去,其实内核中已经有相关接口了,需要做一些小改动。 1.不采用设备树的方式去配置,将720p和1080p的timing参数配置放在数组中。 2.这里的printer和设备树中的.compatible = "printer"一致。 3.将其添加到connector中去 static int panel_simple_get_fixed_modes(struct

weixin_42097108的博客 864

Microservices Event Sourcing开发者指南:构建端到端微服务的最佳实践

想要掌握微服务架构的核心技术吗?Microservices Event Sourcing项目为你提供了一个完美的学习平台!这是一个基于Spring Cloud生态系统的完整微服务架构实现,专门针对在线购物网站场景,展示了如何通过事件溯源(Event Sourcing)和CQRS模式构建高可扩展、最终一致性的分布式系统。无论你是微服务新手还是希望深化理解的开发者,这个项目都能帮助你快速掌握端到端微服

gitblog_00097的博客 633

【雕爷学编程】Arduino FOC 之 AS5600 磁编码器的模拟电压与IIC通信

在Arduino平台上实现FOC可以提供平滑的运行和高度的扭矩、速度和位置控制,它通过精确控制电机的电流和电压来实现高效率、高精度和低噪声的操作。案例三在使用I2C通信读取角度值的基础上,添加了设置角度范围的功能。5、创新:Arduino可以让你用电子的方式来表达你的创意和想象,你可以用Arduino来制作各种有趣和有用的项目,如机器人、智能家居、艺术装置等。1、高性能电机控制:FOC是一种高级的电机控制算法,可以精准控制PMSM(永磁同步电机)和BLDC(无刷直流)电机,实现平滑的转速和扭矩输出。

雕爷学编程 1500

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

如何用FastAPI-Backend-Template实现RESTful API:Account模块CRUD操作全流程

你是否正在寻找一个现代化的Python后端开发模板?FastAPI-Backend-Template正是你需要的解决方案!这个基于FastAPI的后端项目模板提供了完整的RESTful API实现,特别适合快速构建用户管理系统。本文将详细介绍如何使用这个模板实现Account模块的完整CRUD(创建、读取、更新、删除)操作流程。 ## 🚀 项目概述与快速开始 FastAPI-Backend-

gitblog_00187的博客 776

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Docker Python 微服务实用手册(四)

我们谈到了单个微服务领域内的更改请求。但是,如果我们需要部署在两个或多个微服务中运行的功能,该怎么办呢?这种类型的功能应该相对罕见,并且是与单体应用程序相比微服务中的开销的主要原因之一。在单体应用程序中,这种情况根本不可能发生,因为一切都包含在单体应用程序的墙内。与此同时,在微服务架构中,这是一个复杂的更改。这至少涉及到每个相关微服务中的两个独立功能,这些功能位于两个不同的存储库中。很可能这些存储库将由两个不同的团队开发,或者至少负责每个功能的人将不同。

龙哥盟 1081

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

10分钟上手Microservices Event Sourcing:基于Spring Cloud的快速开始教程

想要快速掌握微服务架构中的事件溯源(Event Sourcing)和CQRS模式吗?Microservices Event Sourcing项目为你提供了一个完美的学习平台!这个基于Spring Cloud的在线购物网站项目,不仅展示了微服务的最佳实践,还深入实现了事件溯源和最终一致性,是学习现代分布式系统的绝佳案例。🚀 ## 📋 什么是Microservices Event Sourcin

gitblog_00323的博客 446

agents24 后端系统架构 Agent 全解析:backend-architect 的可扩展 API 设计与微服务架构能力手册

> 本文深度剖析多 Harness Agent 插件市场中 `backend-development` 插件内置的 **backend-architect** 专用 Agent:从其在 [plugins/backend-development/agents/backend-architect.md](https://link.gitcode.com/i/777cf8087cc82b29695731

gitblog_00992的博客 611

Label Studio ML Backend深度解析:构建智能标注系统的技术架构与实战应用

你是否曾面临这样的困境:团队投入大量人力进行数据标注,但标注质量参差不齐,模型迭代周期漫长,标注工具与机器学习模型之间存在着难以逾越的技术鸿沟?在当今数据驱动的AI时代,高效、准确的标注流程已成为模型成功的关键瓶颈。Label Studio ML Backend正是为解决这一核心痛点而设计的技术桥梁,它将机器学习模型无缝集成到标注工作流中,实现了从数据标注到模型优化的全链路自动化。 ## 核心理

gitblog_01049的博客 1143

Buzz 完整指南:离线语音转文字,产出带时间戳字幕

一期播客丢在桌面,几分钟后拿到一份带时间戳的 SRT 字幕文件,音频全程不出本机。Buzz 是构建于 OpenAI Whisper 模型之上的本地语音转文字工具,离线转写、带时间戳字幕导出、翻译任务都能做。 ## Buzz 能做什么 Buzz 覆盖本地转写的完整链路,能力清单如下: - 本地离线转写——音视频文件不离开你的电脑,适合处理采访和会议录音的人 - 带时间戳字幕导出——TXT、SR

gitblog_00332的博客 721

NetBox 用户模型(User)详解:账户体系、权限继承与认证集成

用户(User)是 NetBox 中代表独立个人账户的核心模型:每个用户通过用户名与密码完成认证,可以持有多个 API Token 用于 REST 与 GraphQL API 访问,并通过直接授予或组继承的方式获得对象级权限。本文基于 [用户模型文档](https://link.gitcode.com/i/fed89a663987cdd0fbe26091396f2985) 与 [users 应用源

gitblog_00374的博客 777

Spring Cloud Alibaba 微服务的配置文件apllication.yml中配置项目端口无效问题

检查了项目启动日志,发现配置文件中其他内容生效了,故排除情况1。检查target目录中的缓存文件application.ymal,发现端口配置确实是8102,排除情况3。检查nacos配置文件,并未修改端口,排除情况4。检查端口占用情况,发现8102空闲,排除情况5。由于该微服务仅有这一个配置文件,考虑是否其他微服务的配置文件对其产生影响。清空该文件,重启项目,成功运行于8102。

hahahah25的博客 615

Easy-Vibe 后端架构实战:从脚本到微服务的项目架构选型与演进指南

> 本指南是 Easy-Vibe 后端知识体系中的架构总纲,对应仓库文档 [backend-project-architecture.md](https://link.gitcode.com/i/22b64987207d6fe9afd3b62d9d6fac61)。它以"按规模选架构、按语言定风格"为主线,系统讲解从单文件脚本、分层架构到微服务的完整演进路径,并给出 Node.js、Python、G

gitblog_00399的博客 401

从脚本到微服务:easy-vibe 视角下的后端项目架构选型与渐进式演进指南

> **导读**:后端项目该用单文件脚本、四层分层架构还是微服务,取决于用户规模、团队结构与所选语言的设计哲学。本文以 easy-vibe 的 [backend-project-architecture.md](https://link.gitcode.com/i/ef6dd4e30a24b0d7470fa091e6c0dd44) 为核心骨架,结合仓库中 [backend-layered-arch

gitblog_00818的博客 352

mac-precision-touchpad:如何 3 步给苹果触控板装上三指手势与双指缩放

mac-precision-touchpad 让苹果触控板变成 Windows 精准触控板,装上即用三指拖拽与双指缩放。 ## 先看适不适合你 - 适合:MacBook 或 Magic Trackpad 2 接 Windows 10 及以上,想要三指拖拽、四指切桌面、捏合缩放等手势。 - 不适合:Windows 自带触控板或非苹果第三方触控板,该驱动只认苹果硬件 ID。 - T2 芯片机型走

gitblog_00412的博客 666
上一篇: 微服务(三)—— MyBatis逆向工程模块(backend-generator)
下一篇: 微服务(五)—— 产品模块(backend-product)
CodAlun
博客等级 码龄8年 46粉丝 96原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值