微服务实战(四):用户操作

1、用户服务

(1)UserService接口

public interface UserService {

    /**
     * 登录
     *
     * @param loginRequest
     * @param request
     * @return
     */
    JsonData login(UserLoginReq loginRequest, HttpServletRequest request);

    /**
     * 查询用户详情
     *
     * @return
     */
    UserVO findUserInfo(int userId);

    /**
     * 新增或修改
     *
     * @param userReq
     * @return
     */
    JsonData saveOrUpdate(UserReq userReq);

    /**
     * 删除
     *
     * @param username
     */
    void delete(String username);

    /**
     * 密码重置
     *
     * @param userId
     */
    void reset(Integer userId);

    /**
     * 分页列表
     *
     * @param params
     * @return
     */
    PageResult pageList(Map<String, Object> params);

    /**
     * 修改密码
     *
     * @param oldPwd
     * @param newPwd
     * @param userId
     */
    void modify(String oldPwd, String newPwd, int userId);

    /**
     * 退出
     */
    void logout();

    /**
     * 分配角色
     *
     * @param userRoleReq
     */
    void addUserRoles(UserRoleReq userRoleReq);

}
@Data
@ApiModel(value = "用户", description = "用户请求对象")
public class UserReq {

    @ApiModelProperty(value = "用户id", example = "1")
    private Integer id;

    @ApiModelProperty(value = "用户名", example = "admin", required = true)
    private String username;

    @ApiModelProperty(value = "昵称", example = "管理员", required = true)
    private String nickname;

}
@Data
@ApiModel(value = "用户登录", description = "用户登录请求对象")
public class UserLoginReq {

    @ApiModelProperty(value = "用户名", example = "admin")
    private String username;

    @ApiModelProperty(value = "密码", example = "123456")
    private String password;

    @ApiModelProperty(value = "验证码", example = "123456")
    private String captcha;

}
@Data
@ApiModel(value = "用户角色", description = "用户分配角色请求对象")
public class UserRoleReq {

    @ApiModelProperty(value = "用户id", example = "1")
    @JsonProperty("user_id")
    private int userId;

    @ApiModelProperty(value = "角色列表", example = "[1,2]")
    @JsonProperty("role_ids")
    private List<Integer> roleIds;

}

(2)接口实现UserServiceImpl


@Slf4j
@Service
public class UserServiceImpl implements UserService {

    private static final String INIT_PASSWORD = "123456";

    @Value("${user.head.img}")
    private String defaultHeadImg;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private MenuMapper menuMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 用户登录
     *
     * @param loginRequest
     * @return
     */
    @Override
    public JsonData login(UserLoginReq loginRequest, HttpServletRequest request) {
        String key = getCaptchaKey(request);
        String cacheCaptcha = stringRedisTemplate.opsForValue().get(key);
        // 校验图形验证码
        if (StringUtils.isNoneBlank(cacheCaptcha)) {
            if (loginRequest.getCaptcha() != null
                    && Objects.requireNonNull(cacheCaptcha).equalsIgnoreCase(loginRequest.getCaptcha())) {
                stringRedisTemplate.delete(key);
            } else {
                return JsonData.buildResult(BizCodeEnum.CODE_CAPTCHA_ERROR);
            }
        } else {
            return JsonData.buildResult(BizCodeEnum.CODE_CAPTCHA_NOT_EXIST);
        }

        // 通过username找数据库记录
        SysUserDO sysUserDO = userMapper.selectOne(new QueryWrapper<SysUserDO>().eq("username", loginRequest.getUsername()));
        if (sysUserDO != null) {
            // 获取盐和当前传递的密码就行,加密后匹配
            String cryptPwd = Md5Crypt.md5Crypt(loginRequest.getPassword().getBytes(), sysUserDO.getSecret());
            if (cryptPwd.equals(sysUserDO.getPwd())) {
                // 登录成功,生成token
                String token = geneUserToken(sysUserDO);

                return JsonData.buildSuccess(token);
            } else {
                return JsonData.buildResult(BizCodeEnum.ACCOUNT_PWD_ERROR);
            }
        } else {
            // 为了混淆,一种常用的安全防护
            return JsonData.buildResult(BizCodeEnum.ACCOUNT_PWD_ERROR);
        }
    }

    /**
     * 查询当前用户详情
     *
     * @return
     */
    @Override
    public UserVO findUserInfo(int userId) {
        if (userId < -1L) {
            throw new BizException(BizCodeEnum.ACCOUNT_UNREGISTER);
        }

        int id = userId;
        if (userId == -1L) {
            LoginUser loginUser = LoginInterceptor.threadLocal.get();
            id = loginUser.getId();
        }

        SysUserDO sysUserDO = userMapper.selectOne(new QueryWrapper<SysUserDO>().eq("id", id));
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(sysUserDO, userVO);

        /*
          这里是可以直接写在RoleMapper中的,但是需要在RoleDO中增加两个list字段,会导致后面使用工具类中的方法时,
          RoleDO的字段对应不上。数据表中并没有两个list字段。现在这种处理方式也没什么问题
         */
        // 获取用户角色列表,一般就1到2个角色
        List<SysRoleDO> sysRoleDOList = roleMapper.findRoleListByUserId(id);
        userVO.setRoleList(sysRoleDOList.stream().map(roleDo -> {
            // 查询角色菜单列表
            List<SysMenuDO> sysMenuDOList = menuMapper.findMenuListByRoleId(roleDo.getId());
            // 查询角色权限列表
            List<SysPermissionDO> sysPermissionDOList = permissionMapper.findPermissionListByRoleId(roleDo.getId());

            RoleVO roleVO = new RoleVO();
            BeanUtils.copyProperties(roleDo, roleVO);
            roleVO.setMenuList(sysMenuDOList.stream().map(m -> ConvertBean.getProcess(m, MenuVO.class)).collect(Collectors.toList()));
            roleVO.setPermissionList(sysPermissionDOList.stream().map(m -> ConvertBean.getProcess(m, PermissionVO.class)).collect(Collectors.toList()));

            return roleVO;
        }).collect(Collectors.toList()));

        return userVO;
    }

    /**
     * 用户新增或修改
     *
     * @param userReq
     * @return
     */
    @Override
    public JsonData saveOrUpdate(UserReq userReq) {
        String username = userReq.getUsername();
        String nickname = userReq.getNickname();

        if (userReq.getId() != null && userReq.getId() > 0) {
            // 修改
            SysUserDO sysUserDO = userMapper.selectById(userReq.getId());

            if (sysUserDO != null) {
                // 不允许修改管理员的账号
                if (sysUserDO.getUsername().equals("admin") && !userReq.getUsername().equals("admin")) {
                    return JsonData.buildResult(BizCodeEnum.ACCOUNT_REJECT_UPDATE);
                }

                if (checkUserUnique(username, userReq.getId())) {
                    sysUserDO.setUsername(username);
                    sysUserDO.setNickname(nickname);

                    userMapper.updateById(sysUserDO);

                    geneUserToken(sysUserDO);
                } else {
                    return JsonData.buildResult(BizCodeEnum.ACCOUNT_REPEAT);
                }
            } else {
                return JsonData.buildResult(BizCodeEnum.ACCOUNT_UNREGISTER);
            }
        } else {
            if (StringUtils.isBlank(username)) {
                return JsonData.buildError("用户名不能为空");
            }

            if (StringUtils.isBlank(nickname)) {
                return JsonData.buildError("昵称不能为空");
            }

            // 新增
            synchronized (this) {
                if (checkUserUnique(username, -1)) {
                    SysUserDO sysUserDO = new SysUserDO();
                    sysUserDO.setCreateTime(CommonUtil.getCurrentDate());
                    sysUserDO.setSecret("$1$" + CommonUtil.getStringNumRandom(8));
                    sysUserDO.setPwd(Md5Crypt.md5Crypt(INIT_PASSWORD.getBytes(), sysUserDO.getSecret()));
                    sysUserDO.setUsername(username);
                    sysUserDO.setNickname(nickname);
                    // 从数据表查一下已有的头像
                    String headImg = userMapper.selectUserHeadImg();
                    sysUserDO.setHeadImg(StringUtils.isNotBlank(headImg) ? headImg : defaultHeadImg); // 用户头像

                    userMapper.insert(sysUserDO);
                } else {
                    return JsonData.buildResult(BizCodeEnum.ACCOUNT_REPEAT);
                }
            }
        }

        return JsonData.buildSuccess();
    }

    private boolean checkUserUnique(String username, int userId) {
        QueryWrapper<SysUserDO> queryWrapper = new QueryWrapper<SysUserDO>().eq("username", username).ne("id", userId);
        SysUserDO sysUserDO = userMapper.selectOne(queryWrapper);
        return sysUserDO == null;
    }

    /**
     * 用户分页列表
     *
     * @param params
     * @return
     */
    @Override
    public PageResult pageList(Map<String, Object> params) {
        if (CommonUtil.validatePageSize(params)) {
            throw new BizException(BizCodeEnum.REQUEST_PARAMS_ERROR);
        }
        int page = MapUtils.getInteger(params, "page");
        int size = MapUtils.getInteger(params, "size");

        String username = (String) params.get("username");
        String nickname = (String) params.get("nickname");

        Page<SysUserDO> pageInfo = new Page<>(page, size);
        IPage<SysUserDO> userDOIPage = userMapper.selectPage(pageInfo, new QueryWrapper<SysUserDO>().orderByDesc("id")
                .like(StringUtils.isNotBlank(username), "username", username)
                .like(StringUtils.isNotBlank(nickname), "nickname", nickname));

        long total = userDOIPage.getTotal();
        long pages = userDOIPage.getPages();
        Object data = userDOIPage.getRecords().stream().map(m -> ConvertBean.getProcess(m, UserVO.class)).collect(Collectors.toList());

        return PageResult.builder().totalRecord(total).totalPage(pages).currentData(data).build();
    }

    /**
     * 用户删除
     *
     * @param username
     */
    @Override
    public void delete(String username) {
        if (username.equals("admin")) {
            throw new BizException(BizCodeEnum.ACCOUNT_REJECT_DELETE);
        }

        QueryWrapper queryWrapper = new QueryWrapper<SysUserDO>().eq("username", username);
        SysUserDO sysUserDO = userMapper.selectOne(queryWrapper);

        if (sysUserDO != null) {
            stringRedisTemplate.delete(TOKEN_KEY + sysUserDO.getId());
            userMapper.delete(queryWrapper);
        }
    }

    /**
     * 重置密码
     *
     * @param userId
     */
    @Override
    public void reset(Integer userId) {
        SysUserDO sysUserDO = userMapper.selectById(userId);
        if (sysUserDO != null) {
            if (sysUserDO.getUsername().equals("admin")) {
                throw new BizException(BizCodeEnum.ACCOUNT_REJECT_RESET);
            }

            sysUserDO.setSecret("$1$" + CommonUtil.getStringNumRandom(8));
            sysUserDO.setPwd(Md5Crypt.md5Crypt(INIT_PASSWORD.getBytes(), sysUserDO.getSecret()));

            geneUserToken(sysUserDO);

            userMapper.updateById(sysUserDO);
        } else {
            throw new BizException(BizCodeEnum.ACCOUNT_UNREGISTER);
        }
    }

    /**
     * 修改当前用户的密码
     *
     * @param oldPwd
     * @param newPwd
     * @param userId
     */
    @Override
    public void modify(String oldPwd, String newPwd, int userId) {
        if (oldPwd.equals(newPwd)) {
            throw new BizException(BizCodeEnum.ACCOUNT_PWD_SAME);
        }

        if (newPwd.length() < 6) {
            throw new BizException(BizCodeEnum.ACCOUNT_PWD_LESS);
        }

        SysUserDO sysUserDO = userMapper.selectById(userId);
        if (sysUserDO != null) {
            if (Md5Crypt.md5Crypt(oldPwd.getBytes(), sysUserDO.getSecret()).equals(sysUserDO.getPwd())) {
                sysUserDO.setPwd(Md5Crypt.md5Crypt(newPwd.getBytes(), sysUserDO.getSecret()));
                // 重新生成token
                geneUserToken(sysUserDO);

                userMapper.updateById(sysUserDO);
            } else {
                throw new BizException(BizCodeEnum.ACCOUNT_OLD_PWD_ERROR);
            }
        } else {
            throw new BizException(BizCodeEnum.ACCOUNT_UNREGISTER);
        }
    }

    /**
     * 用户退出
     */
    @Override
    public void logout() {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();
        stringRedisTemplate.delete(TOKEN_KEY + loginUser.getId());

        LoginInterceptor.threadLocal.remove();
    }

    /**
     * 分配用户角色
     *
     * @param userRoleReq
     */
    @Transactional(rollbackFor = BizException.class)
    @Override
    public void addUserRoles(UserRoleReq userRoleReq) {
        userMapper.deleteUserRole(userRoleReq.getUserId());
        userMapper.addUserRoles(userRoleReq.getUserId(), userRoleReq.getRoleIds());
    }

    private String geneUserToken(SysUserDO sysUserDO) {
        LoginUser loginUserDTO = LoginUser.builder().build();
        BeanUtils.copyProperties(sysUserDO, loginUserDTO);
        String token = JWTUtil.geneJsonWebToken(loginUserDTO, sysUserDO.getPwd());
        stringRedisTemplate.opsForValue().set(TOKEN_KEY + sysUserDO.getId(), token, 7, TimeUnit.DAYS);

        return token;
    }
}
@Data
public class UserVO {

    private Integer id;

    /**
     * 昵称
     */
    private String nickname;

    /**
     * 用户名
     */
    private String username;

    /**
     * 头像
     */
    @JsonProperty("head_img")
    private String headImg;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh", timezone = "GMT+8")
    @JsonProperty("create_time")
    private Date createTime;

    /**
     * 用户角色列表
     */
    @JsonProperty("role_list")
    private List<RoleVO> roleList;

}

(3)UserMapper

public interface UserMapper extends BaseMapper<SysUserDO> {

    void updateHeadImg(String headImg);

    void deleteUserRole(int userId);

    void addUserRoles(int userId, @Param("list") List<Integer> roleIds);

    String selectUserHeadImg();
}
    <insert id="addUserRoles">
        insert into user_role(user_id, role_id) values
        <foreach collection="list" item="item" index="index" separator=",">
            (#{arg0},#{item})
        </foreach>
    </insert>
    <update id="updateHeadImg" parameterType="java.lang.String">
        update sys_user
        set head_img = #{arg0}
    </update>
    <delete id="deleteUserRole">
        delete
        from user_role
        where user_id = #{arg0}
    </delete>
    <select id="selectUserHeadImg" resultType="java.lang.String">
        select head_img
        from sys_user
        where head_img != '' limit 1
    </select>

(4)阿里云OSS配置

@Configuration
public class AliyunOSSConfig {

    @Value("${aliyun.oss.endpoint}")
    private String endpoint;
    @Value("${aliyun.oss.access-key}")
    private String accessKeyId;
    @Value("${aliyun.oss.accessKeySecret}")
    private String accessKeySecret;

    @Bean
    @ConditionalOnProperty(name = "aliyun.oss.access-key", matchIfMissing = true)
    public OSS ossClient() {
        return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    }

}

(5)拦截器配置

/**
 * 拦截器配置
 */
@Configuration
@Slf4j
public class InterceptorConfig implements WebMvcConfigurer {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor(stringRedisTemplate))
                // 拦截的路径
                .addPathPatterns("/api/user/*/**", "/api/menu/*/**", "/api/permission/*/**", "/api/role/*/**")
                // 排查不拦截的路径
                .excludePathPatterns("/api/user/*/login");
    }

    @Bean("requestInterceptor")
    public RequestInterceptor requestInterceptor() {
        return template -> {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                template.header("token", request.getHeader("token"));
            } else {
                log.warn("requestInterceptor() 获取Header空指针异常");
            }
        };
    }
}

(6)Controller


@Api(tags = "用户模块")
@RestController
@RequestMapping("/api/user/v1")
public class UserController {
    @Autowired
    private UserService userService;

    @Autowired
    private FileService fileService;

    @ApiOperation("用户头像上传")
    @PostMapping(value = "upload")
    public JsonData uploadHeaderImg(@ApiParam(value = "文件上传", required = true) @RequestPart("file") MultipartFile file) {
        String result = fileService.uploadUserHeadImg(file);
        return result != null ? JsonData.buildSuccess(result) : JsonData.buildResult(BizCodeEnum.FILE_UPLOAD_USER_IMG_FAIL);
    }

    @ApiOperation("用户登录")
    @PostMapping("login")
    public JsonData login(@RequestBody UserLoginReq loginRequest, HttpServletRequest request) {
        return userService.login(loginRequest, request);
    }

    @ApiOperation("用户退出")
    @PostMapping("logout")
    public JsonData logout() {
        userService.logout();
        return JsonData.buildSuccess();
    }

    @ApiOperation("查询用户信息")
    @GetMapping("info")
    public JsonData info(@ApiParam(name = "user_id", value = "用户id,为-1时查询当前登录的用户信息") @RequestParam("user_id") int userId) {
        return JsonData.buildSuccess(userService.findUserInfo(userId));
    }

    @ApiOperation("用户新增或修改")
    @PostMapping("saveOrUpdate")
    public JsonData saveOrUpdate(@RequestBody UserReq userReq) {
        return userService.saveOrUpdate(userReq);
    }

    @ApiOperation("用户分页列表")
    @GetMapping("page_list")
    public JsonData pageList(@RequestParam Map<String, Object> params) {
        return JsonData.buildSuccess(userService.pageList(params));
    }

    @ApiOperation("删除用户")
    @DeleteMapping("delete/{username}")
    public JsonData delete(@PathVariable("username") String username) {
        try {
            userService.delete(username);
            return JsonData.buildSuccess();
        } catch (Exception e) {
            return JsonData.buildError("删除失败:" + e.getMessage());
        }
    }

    @ApiOperation("重置密码")
    @PutMapping("pwd/reset")
    public JsonData resetPwd(@RequestParam("user_id") Integer userId) {
        try {
            userService.reset(userId);
            return JsonData.buildSuccess();
        } catch (BizException e) {
            return JsonData.buildError("密码重置失败:" + e.getMessage());
        }
    }

    @ApiOperation("修改密码")
    public JsonData modifyPwd(@RequestParam("old_pwd") String oldPwd, @RequestParam("new_pwd") String newPwd, @RequestParam("user_id") int userId) {
        try {
            userService.modify(oldPwd, newPwd, userId);
            return JsonData.buildSuccess();
        } catch (BizException e) {
            return JsonData.buildError("密码修改失败:" + e.getMessage());
        }
    }

    @ApiOperation("用户分配角色")
    @PostMapping("addUserRoles")
    public JsonData addUserRoles(@RequestBody UserRoleReq request) {
        try {
            userService.addUserRoles(request);
            return JsonData.buildSuccess();
        } catch (Exception e) {
            return JsonData.buildError("操作失败:" + e.getMessage());
        }
    }

}

2、图形验证码Captcha

 (1)配置

在config包中新增验证码配置,之前已添加过依赖了

@Configuration
public class CaptchaConfig {
    /**
     * 验证码配置
     * Kaptcha配置类名
     */
    @Bean
    @Qualifier("captchaProducer")
    public DefaultKaptcha kaptcha() {
        DefaultKaptcha kaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 验证码个数
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
        // 字体间隔
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "8");
        // 干扰实现类
        properties.setProperty(Constants.KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
        // 图片样式
        properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.WaterRipple");
        // 文字来源
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING, "0123456789");
        Config config = new Config(properties);
        kaptcha.setConfig(config);
        return kaptcha;
    }
}

(2)Controller

@Api(tags = "图形验证码模块")
@RestController
@RequestMapping(("/api/validate/v1"))
@Slf4j
public class ValidateCodeController {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private Producer captchaProducer;

    /**
     * 1分钟过期
     */
    private static final long CAPTCHA_CODE_EXPIRED = 60 * 1000;

    @ApiOperation("获取图形验证码")
    @GetMapping("captcha")
    public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {
        String cacheKey = getCaptchaKey(request);
        String capText = captchaProducer.createText();
        // 存储
        stringRedisTemplate.opsForValue().set(cacheKey, capText, CAPTCHA_CODE_EXPIRED, TimeUnit.MILLISECONDS);
        BufferedImage bi = captchaProducer.createImage(capText);
        ServletOutputStream out;
        try {
            response.setDateHeader("Expires", 0);
            response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
            response.addHeader("Cache-Control", "create_date-check=0, pre-check=0");
            response.setHeader("Pragma", "no-cache");
            out = response.getOutputStream();
            ImageIO.write(bi, "jpg", out);
            out.flush();
            out.close();
        } catch (IOException e) {
            log.error("获取验证码失败:{}", e);
        }
    }
}

下一次,我们来使用Postman测试接口功能!获取验证码->登录->访问接口!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值