若依框架免密登入

需求:

使用若依框架给小程序微信用户提供一个登录的接口,由于微信用户中的唯一标识是openId,这个情况来说,微信直接用openId就能直接登录,也就是直接openId和用户名绑定,免密登入

思路:

1.微信小程序登入code获取openId(不讲)

2.通过openId查询用户是否存在,存在,登录,不存在,新建用户,然后登入(不讲)

 因为我们的用户中密码是SHA-256 +随机盐+密钥对密码进行加密存储的,是不能通过数据库获取的。所以要实现免密登入。

但是如果用用户名就能够直接登入那未免不够安全,所以这里的解决方案是:

3.在登入的时候生成一个code,在生成code的时候存储到redis中,然后在重写若依的登入判断,通过code代替密码实现免密登入

(此情况同样可以在短信验证中使用)

代码实现:

重写若依框架部分:

SysLoginService

添加方法

/**
     * 登录验证
     *  暂时是通过用户名直接验证
     *   code是生成后立即存储在redis中,然后在校验的时候获取redis中的code进行校验
     *   扩展,这里是复用到第三方短信验证的方法
     * @param username 用户名
     * @param code 验证码
     * @return token
     */
    public String login(String username,String code){
        /// 用户验证
        Authentication authentication = null;
        try {
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, code);
        //AuthenticationContextHolder.setContext(token);
        // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(token);
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                throw new UserPasswordNotMatchException();
            } else {
                throw new RuntimeException(e.getMessage());
            }
        }
        Object principal = authentication.getPrincipal();
        LoginUser loginUser = (LoginUser) principal;
        // 生成token
        return tokenService.createToken(loginUser);
    }

MyAuthenticationProvider

添加 MyAuthenticationProvider 做密码校验

package com.ruoyi.framework.config;

import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.framework.web.service.UserDetailsServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class MyAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException, UserPasswordNotMatchException {
        String name = authentication.getName();
        String password = (String) authentication.getCredentials();
        log.info("name:"+name+"password:"+password);
        //这里直接判断异常
        UserDetails user = userDetailsService.loadUserByUsername(name);
        String encoderPassword = bCryptPasswordEncoder.encode(password);
        log.info(encoderPassword);
        // 数据库账号密码的校验能通过就通过

        if (bCryptPasswordEncoder.matches(password, user.getPassword())) {
            log.info("使用账户密码登录");
            return new UsernamePasswordAuthenticationToken(user, encoderPassword);
        }

        //这里是第三方登录的使用方式,用code代替了password的位置
        boolean checkValid = checkValid(name, password);
        log.info(""+checkValid);
        if (checkValid && null != user) {
            log.info("使用了验证码登录");
            return new UsernamePasswordAuthenticationToken(user, password);
        } else {
            // 如果都登录不了,就返回异常输出
            throw new UserPasswordNotMatchException();
        }
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }


    /**
     * 非账号密码登录-验证合法
     * @param userName 用户名
     * @param pwd 第三方校验码
     */
    public boolean checkValid(String userName, String pwd) {
        Object code = redisCache.getCacheObject(userName+"_code");
        if (null != code &&  code.toString().equals(pwd)) {
            return true;
        }
        return false;
    }
}

SecurityConfig

SecurityConfig中添加对应配置

 /**
     * 身份认证接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception
    {
        //  这里使用的是设置一个通关密码的情况,让所有的账户,根据这个密码都能被登录
        //auth.authenticationProvider(new CustomLoginAuthenticationProvider(userDetailsService));
        //auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
        auth.authenticationProvider(myAuthenticationProvider);
    }

UserDetailsServiceImpl 

只是这样还不行,由于若依中是通过上下文根获取对应的账户和密码,而我们重写的并没有传入上下文根中。

所以在userDetailsService.loadUserByUsername(name)方法中加入对应的判断

@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
    {
        SysUser user = userService.selectUserByUserName(username);
        if (StringUtils.isNull(user))
        {
            log.info("登录用户:{} 不存在.", username);
            throw new ServiceException(MessageUtils.message("user.not.exists"));
        }
        else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
        {
            log.info("登录用户:{} 已被删除.", username);
            throw new ServiceException(MessageUtils.message("user.password.delete"));
        }
        else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
        {
            log.info("登录用户:{} 已被停用.", username);
            throw new ServiceException(MessageUtils.message("user.blocked"));
        }
        //在若依自带的login中是将token放入了上下文根,
        Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();

        if (usernamePasswordAuthenticationToken!=null){
            passwordService.validate(user);
        }
        return createLoginUser(user);
    }

业务方面:

    实现

     /**
     * 通过username获取token的方法
     * 这里是直接生成6位验证码,然后获取token
     * 扩展成短信登录的话需要另外写方法
     * @param userName 用户名
     * @return token
     */    
    @Override
    public String getToken(String userName){
        AjaxResult ajax = AjaxResult.success();
        //生成该用户的code密码
        // 生成令牌
        String code = getCode(userName);
        String token = loginService.login(userName,code);
        ajax.put(Constants.TOKEN, token);
        return token;
    }

     /**
     * 通过username获取6位的验证码
     * @param userName 用户名
     * @return token
     */
    public String getCode(String userName){
        String code = getSixNum();
        // 给用户发送验证码 扩展的话接受第三方接口给用户发送验证码
        // sendMsgUtil.send(username,code)
        redisCache.setCacheObject(userName+"_code",code,60*2, TimeUnit.SECONDS);
        return code;
    }

    /**
     * 生成六位随机数
     * @return 6位字符串
     */
    public static String getSixNum() {
        String str = "0123456789";
        StringBuilder sb = new StringBuilder(4);
        for (int i = 0; i < 6; i++) {
            char ch = str.charAt(new Random().nextInt(str.length()));
            sb.append(ch);
        }
        return sb.toString();
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值