若依框架登录过程解读

 前言:登录之前的生成验证码的过程在我前面已经讲到,这里就不介绍了。

下面我们来看整个登录过程都有哪些操作。

/**
     * 登录
     * @param loginBody
     * @return
     */
    @RequestMapping("/login")
    public AjaxResult loginUser(@RequestBody LoginBody loginBody){
        AjaxResult ajax = AjaxResult.success();
        //生成token
        String token = loginService.login(loginBody.getUsername(),
                        loginBody.getPassword(),loginBody.getCode(),loginBody.getUuid());
        ajax.put("token",token);
        return ajax;

    }

我们可以看出,登录的几乎所有逻辑都在login()方法中,其中我将它分为两部分来讲:

第一部分:对于验证码的验证;

第二部分:用户权限的验证;

1.验证码的验证过程

       1.1 查看验证码的状态(-true 开 -false 关)

        

 boolean enabled = configService.selectCaptchaEnabled();




 public boolean selectCaptchaEnabled() {
        String captchaEnabled = selectConfigByKey("sys.account.captchaEnabled");
        //假如redis缓存查不到,则开启验证码,保证安全性
        if(StringUtils.isEmpty(captchaEnabled)){
            return true;
        }
        //return语句不是若依框架的,原框架是对返回结果进行封装,意思都是一样的。
        return captchaEnabled.equals("yes") ? true : false;
    }

当返回结果为true,对验证码进行验证。

        1.2 对验证码进行验证

//enabled返回true代表验证码开启,则进行验证码的验证.
if(enabled){
        validateCaptcha(username,code,uuid);
        }


public void validateCaptcha(String username, String code, String uuid)
    {
        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid,"");
        String captcha = redisCache.getCacheObject(verifyKey);
        redisCache.deleteObject(verifyKey);
        if(captcha == null){
            //TODO 记录登录信息
        }
        if(!code.equalsIgnoreCase(captcha)){
            //TODO 记录登录信息
        }
    }

 从上边我们可以看出,

        1.他先拼接一个String,值为: captcha_codes + uuid,同时调用nvl(就是封装的一个非空判断的方法)进行处理。

        2.从redis缓存中通过拼接的key值找到对应的value值

        3.删除redis的缓存

        4.若在redis中未找到,或者输入验证码错误,则记录登记的信息。

回顾内容:

同时我们也回顾一下当时在生成验证码时,是怎么放key-value的(在第一篇文章有过程的解读)

//首先他是生成uuid,然后和常量值CAPTCHA_CODE_KEY(captcha_codes)进行拼接
//我们对比上面验证的代码就可以看出两个是对应的。
String uuid = IdUtils.simpleUUID();
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
//这是他存入redis中的操作,key为captcha_codes+uuid,value为code(也就是算术表达式的值)
  redisCache.setCacheObject(verifyKey,code,CacheConstants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);

2.用户认证

这里采用的是Spring Security框架


UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,password);
//通过AuthenticationManager对用户进行认证
authentication = manager.authenticate(authenticationToken);

我通过debug让大家看一下数据的变化:

 我们可以看到principal里面只有username的值

 经过认证之后,可以发现已经原来的principal已经封装了用户的信息,包括权限。

认证源码流程:

执行过程authenticationManager.authenticate(authenticationToken) -> WebSecurityConfigurerAdapter中的this.delegate.authenticate(authentication); ->
ProviderManager中的Authentication authenticate(Authentication authentication) -> provider.authenticate(authentication); ->
AbstractUserDetailsAuthenticationProvider中的this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication); ->
 DaoAuthenticationProvider实现了retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) 最终通过 UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
 返回一个UserDetails对象.

最重要的就是最后一步他执行了loadUserByUsername(username),通过username来查询用户信息,返回UserDetails对象。

3.记录用户登录

 public void recordLoginInfo(Long userId)
    {
        SysUser sysUser = new SysUser();
        //设置(更新)用户id
        sysUser.setUserId(userId);
        //更新用户ip地址
        sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
        //更新用户登录时间
        sysUser.setLoginDate(DateUtils.getNowDate());
        userService.updateUserProfile(sysUser);
    }

4.生成令牌(创建Token)

public String createToken(LoginUser loginUser)
    {
        //利用uuid生成Token(个人建议:如果是新手,则不用封装,
        //直接生成uuid就可,但要利用split去除符号,
        //或者更简单可以利用Random生成也可)
        String token = IdUtils.fastUUID();
        loginUser.setToken(token);
        setUserAgent(loginUser);
        refreshToken(loginUser);

        Map<String, Object> claims = new HashMap<>();
        claims.put(Constants.LOGIN_USER_KEY, token);
        return createToken(claims);
    }

其中setUserAgent方法是为了设置用户的代理信息

 public void setUserAgent(LoginUser loginUser)
    {
        //User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 SLBrowser/8.0.1.1171 SLBChan/11
        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        //ip:127.0.0.1
        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        loginUser.setIpaddr(ip);
        //内网IP
        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
        //browser chrome 9
        loginUser.setBrowser(userAgent.getBrowser().getName());
        //OperatingSystem window 10
        loginUser.setOs(userAgent.getOperatingSystem().getName());
    }

使用UserAgent需要引入一下依赖:

        <!-- 解析客户端操作系统、浏览器等 -->
        <dependency>
            <groupId>eu.bitwalker</groupId>
            <artifactId>UserAgentUtils</artifactId>
        </dependency>

这里是利用ServletHttpRequest来获取消息头里面的"User-Agent"信息。我们可以通过浏览器debug的形式来看一下"User-Agent"里面的内容:

 我们可以从中获取到windows版本、浏览器版本等内容。

/**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(LoginUser loginUser)
    {
        //重新设置最新的时间
        loginUser.setLoginTime(System.currentTimeMillis());
        //重新设置过期时间
        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
        // 根据uuid将loginUser缓存
        String userKey = getTokenKey(loginUser.getToken());
        redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
    }
private String createToken(Map<String, Object> claims)
    {
        String token = Jwts.builder()
   
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, secret).compact();
        return token;
    }

利用jwt创建token(带有前缀和密码验证)

令牌前缀为:

login_user_key

密码加密规则为:

SignatureAlgorithm.HS512

密钥为:

secret: abcdefghijklmnopqrstuvwxyz(在yml文件中可以设置)

5.通过ajax返回给浏览器token

ajax.put(Constants.TOKEN, token);
return ajax;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值