收款定制开发【若依】验证码实现、登录、token鉴权 开源框架学习笔记02

文章目录

一、收款定制开发验证码后端实现

文件位置:com.ruoyi.web.controller.common.CaptchaController

/**     * 收款定制开发生成验证码     */    @GetMapping("/captchaImage")    public AjaxResult getCode(HttpServletResponse response) throws IOException    {        //生成一个Ajax 对象,收款定制开发后端与前端交互都是用Ajax        AjaxResult ajax = AjaxResult.success();        //收款定制开发确定是否开启验证码功能,收款定制开发开启则进入验证码校验,收款定制开发不开启则直接返回Ajax        boolean captchaOnOff = configService.selectCaptchaOnOff();        ajax.put("captchaOnOff", captchaOnOff);        if (!captchaOnOff)        {            return ajax;        }        // 收款定制开发保存验证码信息        //收款定制开发先生成一个uuid,再加上头,就生成了存在redis里面的键值key,组合拼接结果就是:举例:captcha_codes:a9291c53cb99428c81e4e344544877ac        String uuid = IdUtils.simpleUUID();        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;        String capStr = null, code = null;        BufferedImage image = null;        // 生成验证码        //判断是不是数字类型验证码        String captchaType = RuoYiConfig.getCaptchaType();        if ("math".equals(captchaType))        {            //谷歌的验证码文本创建(展示问题@结果)3-1=?@2            String capText = captchaProducerMath.createText();            //问题            capStr = capText.substring(0, capText.lastIndexOf("@"));            //答案            code = capText.substring(capText.lastIndexOf("@") + 1);            //根据问题创建图片            image = captchaProducerMath.createImage(capStr);        }        else if ("char".equals(captchaType))        {            //生成文本类的验证码            capStr = code = captchaProducer.createText();            //创建验证码对应图片            image = captchaProducer.createImage(capStr);        }        /**设置验证码缓存         * set key、code、验证码有效期(分钟)、时间颗粒度,若依默认是2min         */        redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);        // 转换流信息写出        FastByteArrayOutputStream os = new FastByteArrayOutputStream();        try        {            //使用图像编写器(支持给定格式【渲染图像】)将图片写入输出流            ImageIO.write(image, "jpg", os);        }        catch (IOException e)        {            return AjaxResult.error(e.getMessage());        }        ajax.put("uuid", uuid);        //输出流转换为Base64        ajax.put("img", Base64.encode(os.toByteArray()));        return ajax;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

可以设置application.yml 文件修改captchaType为char 可以使用char验证码

二、登录

文件位置:com.ruoyi.web.controller.system.SysLoginController

前端验证码验证成功时,会返回刚刚创建的uuid,登录时,会携带刚刚返回的code和uuid和输入的username 和 password

进入后端操作逻辑,访问的是/login 接口

/**     * 登录方法     *      * @param loginBody 登录信息     * @return 结果     */    @PostMapping("/login")    public AjaxResult login(@RequestBody LoginBody loginBody)    {    	/**         * 之前生成验证码的时候给了成功返回结果+uuid+图片         * 现在请求对象loginBody把uuid放参数中带过来         */        AjaxResult ajax = AjaxResult.success();// 先准备一个成功返回{msg=操作成功, code=200}        // 生成令牌        //login()验证码校验、用户验证、登录日志、生成token        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),                loginBody.getUuid());        ajax.put(Constants.TOKEN, token);// 通过使用hashMap的方法给当前的AjaxResult对象,添加一个元素与值        // {msg, code, token}        return ajax;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

进入这个login() 校验方法

/**     * 登录验证     *      * @param username 用户名     * @param password 密码     * @param code 验证码     * @param uuid 唯一标识     * @return 结果     */    public String login(String username, String password, String code, String uuid)    {        boolean captchaOnOff = configService.selectCaptchaOnOff();        // 验证码开关        if (captchaOnOff)        {   //校验验证码===>*********看下一个方法*******            validateCaptcha(username, code, uuid);        }        // 用户验证        Authentication authentication = null;        try        {            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername            authentication = authenticationManager                    .authenticate(new UsernamePasswordAuthenticationToken(username, password));            //UsernamePasswordAuthenticationToken [Principal=com.ruoyi.common.core.domain.model.LoginUser@748cc4df, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[]]        }        catch (Exception e)        {            if (e instanceof BadCredentialsException)            {                //多线程操作,日志记录登录信息,登陆不成功:user.password.not.match                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));                throw new UserPasswordNotMatchException();            }            else            {                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));                throw new ServiceException(e.getMessage());            }        }        //多线程操作,日志记录登录信息,登陆成功:user.login.success        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));        LoginUser loginUser = (LoginUser) authentication.getPrincipal();//com.ruoyi.common.core.domain.model.LoginUser@748cc4df        //记录登录信息=====修改用户基本信息(最后IP、时间)        recordLoginInfo(loginUser.getUserId());        // 生成token        return tokenService.createToken(loginUser);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

进入这个validateCaptcha(username, code, uuid); 校验方法

/**     * 校验验证码     *      * @param username 用户名     * @param code 验证码     * @param uuid 唯一标识     * @return 结果     */    public void validateCaptcha(String username, String code, String uuid)    {        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;//验证码 redis key+uuid        String captcha = redisCache.getCacheObject(verifyKey);//获取到redis中对应的验证码        redisCache.deleteObject(verifyKey);//删除掉这条验证码        if (captcha == null)        {   //验证码不存在            //异步的执行任务:recordLogininfor登录信息记录            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));            throw new CaptchaExpireException();        }        if (!code.equalsIgnoreCase(captcha))        {   //code不等于验证码            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));            throw new CaptchaException();        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

进入这个createToken(loginUser) 生成方法
Token生成流程:
首先是创建Token,用一个UUID工具类创建了一个随机的uuid,保存到LoginUser对象中。这个LoginUser实现了Spring Security中的UserDetails接口,用于保存登录用户信息。
然后有一个setUserAgent ()方法设置用户代理信息,包括ip地址、登陆地点、浏览器类型和操作系统。
接着有一个refreshToken ()方法设置令牌有效期,可以设置用户的登录时间,token过期时间(默认设置30min)以及登录用户信息缓存到Redis中。
最后生成jwt令牌,该令牌保存了token信息,签名算法是HS512,密钥是在配置文件中配置的。

/**     * 创建令牌     *     * @param loginUser 用户信息     * @return 令牌     */    public String createToken(LoginUser loginUser)    {        String token = IdUtils.fastUUID();        //设置token        loginUser.setToken(token);        //设置用户代理信息        setUserAgent(loginUser);        //刷新令牌有效期        refreshToken(loginUser);        Map<String, Object> claims = new HashMap<>();        claims.put(Constants.LOGIN_USER_KEY, token);        // claims这里只是令牌前缀        return createToken(claims);    }//两个方法这里参数不同参数不同/**     * 从数据声明生成令牌     *     * @param claims 数据声明     * @return 令牌     */    private String createToken(Map<String, Object> claims)    {    //JSON Web Token (JWT)就是一种Token的编码算法        String token = Jwts.builder()                .setClaims(claims)                .signWith(SignatureAlgorithm.HS512, secret).compact();         //至此,token生成        return token;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

二、token过滤器

文件位置:com.ruoyi.web.controller.system.SysLoginController

/** * token过滤器 验证token有效性 * 每个请求过滤器一次OncePerRequestFilter * @author ruoyi */@Componentpublic class JwtAuthenticationTokenFilter extends OncePerRequestFilter{    @Autowired    private TokenService tokenService;    //做内部过滤    @Override    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)            throws ServletException, IOException    {        LoginUser loginUser = tokenService.getLoginUser(request);        /**         * 判断用户存在 和 用户已登录(认证)         */        if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))        {            /**             * token是否过期,相差不足20分钟,自动刷新缓存             */            tokenService.verifyToken(loginUser);            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));            //上下文设置新的认证            SecurityContextHolder.getContext().setAuthentication(authenticationToken);        }        /**         * 导致调用链中的下一个过滤器,或者如果调用 过滤器是链中的最后一个过滤器,则导致调用链末尾的资源。         */        chain.doFilter(request, response);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发