登录和授权实战

1 准备工作

1.1 发送短信服务

1.1.1 接入阿里云短信服务

阿里云短信接入在这里插入图片描述

1.1.2 短信发送工具类

@Component
public class SmsUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(SmsUtils.class);

    @Autowired
    private AcSmsProperties acSmsProperties;

    @Autowired
    private CwosPlatformProperties cwosPlatformProperties;

    /**
     * 通过阿里发送短信
     * @param telephone
     * @param templateId
     * @param templateParam
     * @return
     */
    public boolean sendAliSms(String telephone, String templateId, String templateParam) {
        if (cwosPlatformProperties.isTestMode()) {
            return true;
        }
        //设置超时时间-可自行调整
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
        System.setProperty("sun.net.client.defaultReadTimeout", "10000");
        //替换成你的AK
        //初始化ascClient,暂时不支持多region(请勿修改)
        IClientProfile profile = DefaultProfile
                .getProfile(acSmsProperties.getRegionId(),
                        acSmsProperties.getAccessKeyId(),
                        acSmsProperties.getAccessKeySecret());
        try {
            DefaultProfile.addEndpoint(acSmsProperties.getEndpointName(), acSmsProperties.getRegionId(),
                    acSmsProperties.getProduct(), acSmsProperties.getDomain());
        } catch (ClientException e1) {
            LOGGER.error("短信发送失败,",e1);
        }
        IAcsClient acsClient = new DefaultAcsClient(profile);
        //组装请求对象
        SendSmsRequest request = new SendSmsRequest();
        //使用post提交
        request.setMethod(MethodType.POST);
        //必填:待发送手机号。支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式
        request.setPhoneNumbers(telephone);
        //必填:短信签名-可在短信控制台中找到
        request.setSignName("云从科技");
        //必填:短信模板-可在短信控制台中找到
        request.setTemplateCode(templateId);
        //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
        //友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败
        request.setTemplateParam(templateParam);
        //可选-上行短信扩展码(扩展码字段控制在7位或以下,无特殊需求用户请忽略此字段)
        //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
        request.setOutId("yourOutId");
        //请求失败这里会抛ClientException异常
        SendSmsResponse sendSmsResponse = null;
        try {
            sendSmsResponse = acsClient.getAcsResponse(request);
        } catch (Exception e) {
            LOGGER.error("短信发送失败,",e);
        }
        if (sendSmsResponse != null) {
            if (sendSmsResponse.getCode() != null && "OK".equals(sendSmsResponse.getCode())) {
                LOGGER.info("发送短信成功,phone:{},发送内容:{}",telephone,templateParam);
                return true;
            } else {
                LOGGER.warn("短信发送失败,code:{},message:{}", sendSmsResponse.getCode(), sendSmsResponse.getMessage());
            }
        }
        return false;
    }
}

1.2 发送邮件服务

使用javax.mail发送邮件

@Component
public class EmailUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(EmailUtils.class);

    @Autowired
    private CwosMailProperties cwosMailProperties;

    /**
     * 用户名密码验证,需要实现抽象类Authenticator的抽象方法PasswordAuthentication
     */
     class MyAuthenticator extends Authenticator {
        String u;
        String p;

        public MyAuthenticator(String u, String p) {
            this.u = u;
            this.p = p;
        }

        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(u, p);
        }
    }

    public  Boolean sendQqExmail(String to, String title, String content, EmailSettings settings) {
        Boolean flag = false;
        Properties prop = new Properties();
        //协议
        prop.setProperty("mail.transport.protocol", settings.getProtocol());
        //服务器
        prop.setProperty("mail.smtp.host", settings.getHost());
        //端口
            prop.setProperty("mail.smtp.port", settings.getPort());
        //使用smtp身份验证
        prop.setProperty("mail.smtp.auth", "true");
        prop.put("mail.smtp.starttls.enable", "true");
        prop.put("mail.smtp.starttls.required", "true");
        prop.put("mail.smtp.connectiontimeout", cwosMailProperties.getConnectionTimeout());
        prop.put("mail.smtp.writetimeout", cwosMailProperties.getWriteTimeout());
        prop.put("mail.smtp.ssl.trust", "*");
        //
        Session session = Session.getInstance(prop, new MyAuthenticator(settings.getAccount(), settings.getPassword()));
        session.setDebug(true);
        MimeMessage mimeMessage = new MimeMessage(session);
        try {
            mimeMessage.setFrom(new InternetAddress(settings.getAccount(), settings.getFrom()   ));
            mimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
            mimeMessage.setSubject(title);
            mimeMessage.setSentDate(new Date());
            mimeMessage.setContent(content, "text/html;charset=UTF-8");
            mimeMessage.saveChanges();
            Transport.send(mimeMessage);
            flag = true;
        } catch (MessagingException | UnsupportedEncodingException e) {
            LOGGER.error("邮件发送异常", e);
        }
        return flag;
    }

}

1.3 生成图形验证码

1.3.1 生成验证码

生成的验证码要保存到redis中,用于登录验证。

   @Override
    public Result<AcCaptchaOutDTO> generateCaptcha() throws ServiceException {
        try {
            AcCaptchaDTO acCaptchaDTO= acCaptchaProcessor.generate();
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            ImageIO.write(acCaptchaDTO.getImage(), "png", bout);
            bout.close();
            //生成对应key
            String uuid= AcToolUtils.getUUID();
            //将code存入redis
            acCacheProvider.getAcCacheDao().set(RedisNamespaceConstant.VERIFY_CAPTCHA+ uuid, acCaptchaDTO.getCode(), acCaptchaProperties.getValidSecond());
            AcCaptchaOutDTO acCaptchaOutDTO = new AcCaptchaOutDTO();
            acCaptchaOutDTO.setCaptchaKey(uuid);
            acCaptchaOutDTO.setImgByteArray(bout.toByteArray());
            return  Result.success(acCaptchaOutDTO);
        } catch (Exception e) {
            logger.error("生成图形验证码失败", e);
            throw new ServiceException(RespCodeConstant.WRONG_GENERATE_CODE, this.getMessage(RespCodeConstant.WRONG_GENERATE_CODE));
        }
    }

1.3.2 将验证码的key保存到cookie

将key保存到cookie中,之后用于取redis中的验证码

    @RequestMapping(value = "/captcha", method = {RequestMethod.POST, RequestMethod.GET})
    public Result<AcCaptchaOutDTO> captcha(HttpServletResponse response) {
        try {
            Result<AcCaptchaOutDTO> captchaResult = acCaptchaService.generateCaptcha();
            Cookie cookie = new Cookie("VerificationCode", captchaResult.getData().getCaptchaKey());
            cookie.setMaxAge(1800);
            cookie.setSecure(PlatformProperties.getHttpsEnable());
            cookie.setPath("/" + rootPath + "/portal/account");
            response.addCookie(cookie);
            return Result.success(captchaResult.getData());
        } catch (ServiceException e) {
            return Result.fail(e.getCode(), e.getMessage());
        } catch (Exception e) {
            logger.error("获取验证码失败,原因:", e);
            return Result.fail(AuthRespCodeEnum.RESPONSE_GET_CAPTCHA_FAIL.getCode(),
                    AuthRespCodeEnum.RESPONSE_GET_CAPTCHA_FAIL.getMessage());
        }
    }

2 登录

登录验证用户名和密码,生成的token要保存到redis中,用于网关验证

  // 校验成功,取token
        AcClaimsDTO acClaimsDTO = new AcClaimsDTO(acLoginDTO.getId(), acLoginDTO.getLoginName(),
                acLoginDTO.getTelephone(), acLoginDTO.getEmail(), acLoginDTO.getBusinessId(), loginParam.getSource());
        String tokenKey = String.format(RedisNamespaceConstant.LOGIN_TOKEN_KEY, loginParam.getSource(), acLoginDTO.getId());
        String tokenJson=tokenProvider.generate(tokenKey, JSON.toJSONString(acClaimsDTO),loginParam.getAccessTokenExp(),loginParam.getRefreshTokenExp());
        AcTokenDTO acTokenDTO = JSON.parseObject(tokenJson, AcTokenDTO.class);
        //多端登录处理类型默认为2,不支持多端登陆
        String lastToken = acCacheProvider.getAcCacheDao().get(RedisNamespaceConstant.MULTI_LOGIN_ID +acLoginDTO.getId() + ":" + acClaimsDTO.getSource());
        tokenProvider.clear(lastToken);
        acCacheProvider.getAcCacheDao().set(RedisNamespaceConstant.MULTI_LOGIN_ID+acLoginDTO.getId() + ":" + acClaimsDTO.getSource(), acTokenDTO.getAccessToken());
        acLoginDTO.setAccessToken(acTokenDTO.getAccessToken());
        acLoginDTO.setAccessTokenExp(acTokenDTO.getAccessTokenExp());
        acLoginDTO.setRefreshToken(acTokenDTO.getRefreshToken());
        acLoginDTO.setRefreshTokenExp(acTokenDTO.getRefreshTokenExp());
        if (null != acAccount.getUserResult()) {
        UserResult ldapUserResult = new UserResult();
            acLoginDTO.setUserResult(BeanCopyUtils.copyProperties(acAccount.getUserResult(), ldapUserResult));
        }
        return Result.success(acLoginDTO);
  

3 网关认证

从redis中读取token,和用户提交的token进行比较

private Object loginValidate() {
		RequestContext ctx = RequestContext.getCurrentContext();
		// 先判断路径,如果是登录不需要校验
		String url = ctx.getRequest().getRequestURI();
		Boolean flag = Pattern.matches(".*login|.*token", url);
		logger.info("是否为登录:"+flag);
		if (flag) {
			cookie.setHttpOnly(true);
			cookie.setSecure(true);
			ctx.getResponse().addCookie(cookie);
			return null;
		}
		
		String token = this.getToken();
		logger.info("请求中的token为:"+token);
		if (StringUtils.isNotBlank(token)) {
			
			String passed = redisTemplate.opsForValue().get("JWT_TOKEN_$" + token);
			logger.info("redis存在性验证:"+!(passed == null || passed == ""));
			if (passed == null || passed == "") {
				ctx.setSendZuulResponse(false);
				ctx.setResponseStatusCode(401);
				ctx.setResponseBody(JSONObject.toJSON(CloudwalkResult.fail("56000066", "Token已失效,请重新登录!")).toString());
				ctx.getResponse().setContentType("application/json;charset=UTF-8");
				return null;
			}
			 
			try {
				this.validateToken(token);
				logger.info("token校验正常:"+token);
				//校验成功后刷新TOKEN过期时间
				Claims c = getClaims(token);
				final JSONObject jo = JSONObject.parseObject(JSONObject.toJSONString(c));
				JWTRedisDaoImpl dao = new JWTRedisDaoImpl(redisTemplate);
				redisTemplate.expire("JWT_TOKEN_$" + token, tokenTime*1000, TimeUnit.MILLISECONDS);
			}catch(Exception e) {
				logger.info("token校验异常:"+e+":"+token);
				ctx.setSendZuulResponse(false);
				ctx.setResponseStatusCode(401);
				ctx.setResponseBody(JSONObject.toJSON(CloudwalkResult.fail("56000065", "Token已失效,请重新登录!")).toString());
				ctx.getResponse().setContentType("application/json;charset=UTF-8");
				return null ;
			}
			return null ;
		
		
		}
		ctx.setSendZuulResponse(false);
		ctx.setResponseStatusCode(401);
		ctx.setResponseBody(JSONObject.toJSON(CloudwalkResult.fail("56000065", "Token已失效,请重新登录!")).toString());
		ctx.getResponse().setContentType("application/json;charset=UTF-8");
		return null;
	}

4 权限管理

用户<–>角色<–>权限

4.1 用户管理

添加用户的时候,需要添加账号(登录的时候用),同时要配置用户角色。

4.1.1 用户批量导入

(1)设置模板

 /**
     * 更新模板配置
     *
     * @throws ServiceException
     */
    @Override
    @ParamsValidate(argsIndexs = {0})
    public Result<Boolean> update(BatchConfigParam param, CallContext context)
            throws ServiceException {
        BatchConfig batchConfig = new BatchConfig();
        BeanUtils.copyProperties(param, batchConfig);
        batchConfig.setBusinessId(context.getCompany().getCompanyId());
        batchConfig.setCreateTime(System.currentTimeMillis());
        batchConfig.setCreateUserId(context.getUser().getCaller());

        BatchConfig batchConfigExist = batchConfigMapper.selectByBusinessId(context.getCompany().getCompanyId());
        if (batchConfigExist == null) {
            batchConfig.setId(ToolUtil.generateUUID());
            int count = batchConfigMapper.insert(batchConfig);
            if (count < 1) {
                return CloudwalkResult.fail(BatchRespCodeConstant.BATCHCONFIG_INSERT_ERROR,
                        this.getMessage(BatchRespCodeConstant.BATCHCONFIG_INSERT_ERROR));
            }
        } else {
            int count = batchConfigMapper.update(batchConfig);
            if (count < 1) {
                return CloudwalkResult.fail(BatchRespCodeConstant.BATCHCONFIG_INSERT_ERROR,
                        this.getMessage(BatchRespCodeConstant.BATCHCONFIG_INSERT_ERROR));
            }
        }
        return Result.success(true);
    }

(2)下载模板
导出excel的时候,需要输入header 数据map

 /**
     * <p>Title: createTemplate</p>
     * <p>Description: 创建导出模板</p>
     *
     * @param batchConfig
     * @param context
     * @throws IOException
     * @throws ServiceException
     * @throws ResourceServiceException
     */
    public String createTemplate(BatchConfig batchConfig, CloudwalkCallContext context) throws IOException {
        //获取模板类型、处理表头数据
        List<Map<String, String>> headers = getHeaders(batchConfig.getConfigJson());
        //未获取表头 则用户未配置模板
        if (CollectionUtils.isEmpty(headers)) {
            return "";
        }
        //生成zip包的临时文件夹
        String folderName = ToolUtil.generateUUID();
        //获取生成模板存放的基础路径
        String tempFilePath = cwosDownloadProperties.getTemplatePath() + folderName;
        File tempFile = new File(tempFilePath);
        if (!tempFile.exists()) {
            tempFile.mkdirs();
        }
        //初始化默认数据
        Map<String, String> dataMap = initdata();
        //获取项目中模板所在位置的真实路径   /upload/template/
        String tempRealPath = cwosDownloadProperties.getTemplatePath();
        String imageName = getImg(tempRealPath);
        // 文件名
        if (TemplateType.from(batchConfig.getType()) == TemplateType.FILE_NAME) {
            //图片文件名
            StringBuilder picName = new StringBuilder();
            //根据获取的表头数据,去dataMap中拿到默认数据
            for (int i = 0; i < headers.size(); i++) {
                Map<String, String> map = headers.get(i);
                //根据表头拼接图片文件名称
                if (i == (headers.size() - 1)) {
                    picName.append(dataMap.get(map.get("id")));
                } else {
                    picName.append(dataMap.get(map.get("id")) + "-");
                }
            }
            //将原始样例图片 复制到 即将生成zip包的临时文件夹
            FileUtils.copyFile(new File(tempRealPath + imageName), new File(tempFile.getAbsoluteFile() + File.separator + picName + ".jpg"));
        } else {
            //原始样例图片 copy 到 zip包的临时文件夹
            FileUtils.copyFileToDirectory(new File(tempRealPath + imageName), tempFile);
            //根据模板类型生成相应的模板
            File filename = null;
            if (TemplateType.from(batchConfig.getType()) == TemplateType.CSV) {
                filename = new File(tempFile, "人员批量导入模板.csv");
                exportCsv(filename.getAbsolutePath(), headers, dataMap);
            } else if (TemplateType.from(batchConfig.getType()) == TemplateType.EXCEL) {
                filename = new File(tempFile, "人员批量导入模板.xls");
                exportExcel(filename.getAbsolutePath(), headers, dataMap);
            }
        }

        //将服务器上存放Excel/Csv/图片的文件夹打成zip包
        File zipFile = new File(tempRealPath, folderName + ".zip");
        ZipUtil.zipMultiFile(tempFile.getAbsolutePath(), zipFile.getAbsolutePath(), false,
                batchImportService.getPersonBatchSetting(PersonBatchConstant.FILE_ENCODE, context));
        return tempFilePath;
    }

(3)excel导入

   @Override
    public CloudwalkResult<DictBatchImportResult> batchImport(DictImportParam dictImportParam, CloudwalkCallContext cloudwalkCallContext){
        //校验文件是否存在
        CloudwalkResult<byte[]> fileBytes = fileService.get(dictImportParam.getFilePath());
        if (fileBytes.getData() == null){
            return CloudwalkResult.fail(BatchRespCodeConstant.BATCHIMPORT_FILE_NULL, this.getMessage(BatchRespCodeConstant.BATCHIMPORT_FILE_NULL));
        }
        List<List<String>> result = new ArrayList<>();
        int skipRowIndex = 1;
        ExcelUtils.readFromExcel(result, new ByteArrayInputStream(fileBytes.getData()), 0, skipRowIndex, 0, false);
        //校验是否有数据
        if (result.size() < skipRowIndex + 1){
            return CloudwalkResult.fail(BatchRespCodeConstant.BATCHIMPORT_DATA_EMPTY, this.getMessage(BatchRespCodeConstant.BATCHIMPORT_DATA_EMPTY));
        }
        //校验文件格式
        List<String> titles = result.get(0);
        String[] titleDicts = DictConstant.titleDicts();
        for (int i = 0; i<titleDicts.length ; i++){
            if (titles.size() > i){
                if (!titleDicts[i].equals(titles.get(i).trim())) {
                    return CloudwalkResult.fail(BatchRespCodeConstant.BATCHIMPORT_FILE_FORMAT_ERROR,
                            this.getMessage(BatchRespCodeConstant.BATCHIMPORT_FILE_FORMAT_ERROR));
                }
            } else {
                return CloudwalkResult.fail(BatchRespCodeConstant.BATCHIMPORT_FILE_FORMAT_ERROR,
                        this.getMessage(BatchRespCodeConstant.BATCHIMPORT_FILE_FORMAT_ERROR));
            }
        }

        //第一行信息,为标题信息,typeCode,name,code,status,remark
        result = result.subList(1, result.size());
        List<DictAddParam> addParamList = new ArrayList<>();
        for (List<String> line : result) {
            DictAddParam addParam = getDictAddParam(line);
            addParamList.add(addParam);
        }
        CloudwalkResult<DictBatchImportResult> resultCloudwalkResult;
        try {
            resultCloudwalkResult = dictService.batchImport(addParamList, cloudwalkCallContext, skipRowIndex);
        } catch (ServiceException e) {
            return CloudwalkResult.fail(e.getCode(), getMessage(e.getCode()));
        }
        DictBatchImportResult dictBatchImportResult = resultCloudwalkResult.getData();
        if (dictBatchImportResult.getFailCount() > 0 ){

            byte [] files = writeFailedDict2Stream(dictBatchImportResult.getResultList());
            //写入到文件
            fileService.save(DICT_FILE_INSERT_FAILED_PATH + dictBatchImportResult.getBatchId() + xlsx, files, false);
            dictBatchImportResult.setResultList(null);
            //全部导入失败
            if (dictBatchImportResult.getSuccessCount() ==0){
                CloudwalkResult<DictBatchImportResult> cloudwalkResult = CloudwalkResult.success(dictBatchImportResult);
                cloudwalkResult.setCode(BatchRespCodeConstant.BATCHIMPORT_ALL_ERROR);
                cloudwalkResult.setMessage( getMessage(BatchRespCodeConstant.BATCHIMPORT_ALL_ERROR));
                return cloudwalkResult;
            }
            //部分导入失败
            CloudwalkResult<DictBatchImportResult> cloudwalkResult = CloudwalkResult.success(dictBatchImportResult);
            cloudwalkResult.setCode(BatchRespCodeConstant.BATCHIMPORT_PART_ERROR);
            cloudwalkResult.setMessage( getMessage(BatchRespCodeConstant.BATCHIMPORT_PART_ERROR));
            return cloudwalkResult;
        }
        //全部导入成功
        return resultCloudwalkResult;
    }

4.2 角色管理

4.3 权限管理

一个应用有多个接口,若干接口构成一个资源。分配这些资源就是分配权限。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值