策略模式+工厂模式实现选择多方式登录,微信登录或qq登录,如何实现登录功能,登录的实现

目录

1.创建用户行为的策略接口

2.实现具体行为,实现策略接口

2.1账号登录实现

2.2邮箱登录实现

2.3短信登录实现

3.yml文件中添加登录方式

4.读取yml文件的参数

5.操作策略的上下文环境类 工具类

6.调用

6.1Controller层调用

6.2Service层

6.2实体类


要实现解耦版的登录切换功能,策略模式+工厂模式+ConcurrentHashMap+在Bean的生命周期期间就进行策略绑定,带有Aware的接口进行方法增强

策略模式:策略模式是一种行为型设计模式,定义一系列算法,并使这些算法可以相互替换,使得算法的变化独立于使用算法的客户。策略模式通常包含一个策略接口和多个实现这个接口的策略类。

工厂模式:工厂是创建型模式,它的作用就是创建对象。将对象统一管理,在业务不同时在调用不同的对象进行使用
 

1.创建用户行为的策略接口

package com.example.barterbuddy.strategy;

import com.example.barterbuddy.dto.LoginFromDTO;
import com.example.barterbuddy.dto.Result;
/**
 * 抽象策略类
 */
public interface UserGranter {
    /**
     * 获取数据
     * @param loginReq 传入的参数
     * @return map值
     */
    Result login (LoginFromDTO loginReq);
}

2.实现具体行为,实现策略接口

2.1账号登录实现

package com.example.barterbuddy.strategy;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.example.barterbuddy.dto.LoginFromDTO;
import com.example.barterbuddy.dto.Result;
import com.example.barterbuddy.dto.UserDTO;
import com.example.barterbuddy.pojo.Integral;
import com.example.barterbuddy.pojo.User;
import com.example.barterbuddy.service.IIntegralService;
import com.example.barterbuddy.service.UserService;
import com.example.barterbuddy.utils.PasswordEncoder;
import com.example.barterbuddy.utils.RedisConstants;
import com.example.barterbuddy.utils.RegexUtils;
import com.example.barterbuddy.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static com.example.barterbuddy.utils.RedisConstants.LOGIN_USER_TTL;

/**
 * 	策略:账号登录
 **/
@Component
@Slf4j
public class AccountGranter implements UserGranter {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private UserService userService;
    @Autowired
    private IIntegralService integralService;
    /**
    *<p> </p>
    *密码登录
    *@author panwu
    *@descripion
    *@date 2023/8/19 14:53
    */
    @Override
    public Result login(LoginFromDTO loginReq) {

        if (!RegexUtils.isPhoneInvalid(loginReq.getPhone())) {
            return Result.fail("手机号错误");
        }
        //查询数据库的账号密码是否存在
    /*    User user = userService.query()
                .eq("phone", loginReq.getPhone())
                .eq("password", loginReq.getPassword()).one();
*/
        User user = userService.query()
                .eq("phone", loginReq.getPhone()).one();
        //解密

        Boolean matches = PasswordEncoder.matches(user.getPassword(), loginReq.getPassword());
        log.debug("用户{}的密码{}解密{},查询出的密码为{}",
                loginReq.getPhone(),loginReq.getPassword(),matches,user.getPassword());
        if (!matches){
            return Result.fail("用户名错误或密码不存在");
        }
        String token = UUID.randomUUID().toString();

        //过滤重要信息


        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);

        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));


        //生成token
        String tokenKey = RedisConstants.LOGIN_USER_KEY+token;

        //存入redis
        redisTemplate.opsForHash().putAll(tokenKey,userMap);
        redisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);

        UserHolder.saveUser(userDTO);
        return Result.ok(token);
    }
}

该类中的登录行为你们自己写,不必照我的写

2.2邮箱登录实现

package com.example.barterbuddy.strategy;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.example.barterbuddy.dto.LoginFromDTO;
import com.example.barterbuddy.dto.Result;
import com.example.barterbuddy.pojo.User;
import com.example.barterbuddy.service.UserService;
import com.example.barterbuddy.utils.Email;
import com.example.barterbuddy.utils.PasswordEncoder;
import com.example.barterbuddy.utils.RedisConstants;
import com.example.barterbuddy.utils.RegexUtils;
import org.checkerframework.checker.units.qual.C;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

@Component
/**
*<p> </p>
*邮箱登录
*@author panwu
*@descripion
*@date 2023/8/31 9:01
*/
public class EmailGranter implements UserGranter {
    @Autowired
    private Email emails;
    @Autowired
    private UserService userService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    public Result login(LoginFromDTO loginReq) {
        String email = loginReq.getEmail();
        //判断是否为正确格式
        if (!RegexUtils.isEmailInvalid(email)) {
           return Result.fail("格式不正确");
        }
        String code = redisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + email);
        if (!loginReq.getCode().equals(code)){
            return Result.fail("验证码错误");
        }
//        User one = userService.query().eq("email", email).one();
        //生成新密码
        String password = RandomUtil.randomString(9);
        User user = new User();
        //修改密码
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
        userUpdateWrapper.eq("email",email);
        userUpdateWrapper.set("password", PasswordEncoder.encode(password));
        userService.update(userUpdateWrapper);
        try {
            emails.sendEmail(email,password,"");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.ok();
    }
}

2.3短信登录实现

package com.example.barterbuddy.strategy;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.example.barterbuddy.dto.LoginFromDTO;
import com.example.barterbuddy.dto.Result;
import com.example.barterbuddy.dto.UserDTO;
import com.example.barterbuddy.pojo.Integral;
import com.example.barterbuddy.pojo.User;
import com.example.barterbuddy.service.IIntegralService;
import com.example.barterbuddy.service.UserService;
import com.example.barterbuddy.service.VipService;
import com.example.barterbuddy.service.impl.IntegralServiceImpl;
import com.example.barterbuddy.utils.GeoUtils;
import com.example.barterbuddy.utils.PasswordEncoder;
import com.example.barterbuddy.utils.RegexUtils;
import com.example.barterbuddy.utils.UserHolder;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.example.barterbuddy.utils.RedisConstants.*;

/**
*<p> </p>
*短信登录
*@author panwu
*@descripion
*@date 2023/8/19 13:36
*/
@Component
@Slf4j
public class SmsGranter implements UserGranter{
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private UserService userService;
    @Autowired
    private VipService vipService;
    @Autowired
    private IIntegralService integralService;
    @Override
    public Result login(LoginFromDTO loginFrom) {
        String phone = loginFrom.getPhone();

        // 1.校验手机号
        if (!RegexUtils.isPhoneInvalid(phone)) {
            // 2.如果不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }
        //3.符合 获取验证码
        String cachecode = redisTemplate.opsForValue().get(LOGIN_CODE_KEY+phone);
        String code = loginFrom.getCode();
        if (StrUtil.isEmpty(code) || !code.equals(cachecode)) {
            //验证码为空或者错误
            return Result.fail("请输入验证码");
        }
        //4.验证码正确
        //5.根据手机号查询用户 select * from user where phone=?
        User user =userService.query().eq("phone", phone).one();

        //6.判断用户是否存在
        if (BeanUtil.isEmpty(user)) {
            //不存在用户,创建用户
            user = createUserWithPhone(loginFrom);
        }
        //7.保存用户信息到 redis中
        //7.1.随机生成token,作为登录令牌
        String token= UUID.randomUUID().toString();
        //7.2将User对象转为HashMap存储
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        log.debug("短信登录的用户消息为:{}",userDTO.toString());

        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
        //8.存入redis key=BarterBuddy:login:token:phone
        String tokenkey = LOGIN_USER_KEY + token;
        redisTemplate.opsForHash().putAll(tokenkey, userMap);
        //设置token有效时间 36000L
        redisTemplate.expire(tokenkey, LOGIN_USER_TTL, TimeUnit.MINUTES);

        //存入Threadlocal
        UserHolder.saveUser(userDTO);
        //返回
        return Result.ok(token);
    }

    //创建用户
    private User createUserWithPhone(LoginFromDTO loginFrom) {
        //删除Geo
        redisTemplate.delete("BarterBuddy:cache:user:");

        // 1.创建用户
        User user = new User();
        user.setPhone(loginFrom.getPhone());
        user.setUsername("Buddy_" + RandomUtil.randomString(10));
        user.setPassword(PasswordEncoder.encode("123456"));//默认密码 123456
        //默认头像
        user.setIcon("");
        user.setAddress(loginFrom.getAddress());
        String[] split = loginFrom.getAddress().split(",");
        user.setAddressName(GeoUtils.getAddress(Double.valueOf(split[0]),Double.valueOf(split[1])).get(2));
        // 2.保存用户
        userService.save(user);
        //创建积分表
        Integral integral = new Integral();
        integral.setUserid(user.getId());
        integral.setRecenttime(LocalDateTime.now());
        integral.setScore(10);
        integralService.save(integral);


        Collection<User> list = userService.list();
//        System.out.println("list = " + list);
        // ("id:店名:是否在站长":113.7505,23.0177)
        Map<String, Point> UserMap = list.stream().collect(Collectors.toMap(user1 ->
                        user.getId()+":"+user.getAddressName()+":"+user.getIspresident(),
                user1 -> {
                    String[] split1 = user.getAddress().split(",");
                    Point point = new Point(Double.valueOf(split1[0]), Double.valueOf(split1[1]));
                    return point;
                }));
//        System.out.println("UserMap = " + UserMap);
        //PW TODO : 缓存时间,删除时机
        redisTemplate.opsForGeo().add("BarterBuddy:cache:user:",UserMap);
        return user;
    }
}

3.yml文件中添加登录方式

右边的一定要和Bean名字一样

#登录的自定义信息
login:
  types:
    account: accountGranter #密码登录
    sms: smsGranter #短信登录
    we_chat: weChatGranter #
    email: emailGranter

4.读取yml文件的参数

package com.example.barterbuddy.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.Map;
/**
*<p> </p>
*新增读取数据配置类
*@author panwu
*@descripion
*@date 2023/8/19 13:45
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "login")
public class LoginTypeConfig {

    private Map<String,String> types;

}

 5.操作策略的上下文环境类 工具类

package com.example.barterbuddy.strategy;

import com.example.barterbuddy.config.LoginTypeConfig;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 操作策略的上下文环境类 工具类
 * 将策略整合起来 方便管理
 */
@Component
public class UserLoginFactory implements ApplicationContextAware {

    /**
    *<p> </p>
    *集合存入 及登录的类方法
    *@author panwu
    *@descripion
     * 登录类型:String
     * 类方法: UserGranter
    *@date 2023/8/19 13:51
    */
    private static Map<String, UserGranter> granterPool = new ConcurrentHashMap<>();

    //读取配置信息
    @Autowired
    private LoginTypeConfig loginTypeConfig;


    /**
     * 从配置文件中读取策略信息存储到map中
     * {
     * account:accountGranter,
     * sms:smsGranter,
     * we_chat:weChatGranter
     * }
     *
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        loginTypeConfig.getTypes().forEach((k, y) -> {
            granterPool.put(k, (UserGranter) applicationContext.getBean(y));
        });
    }


    /**
     * 对外提供获取具体策略
     * @param grantType 用户的登录方式,需要跟配置文件中匹配
     * @return 具体策略
     */
    public UserGranter getGranter(String grantType) {
        UserGranter tokenGranter = granterPool.get(grantType);
        return tokenGranter;
    }
}

类中采用了ConcurrentHashMap来保证线程安全,

实现了bean的生命周期中的ApplicationContextAware接口,创建bean的时候进行增强

重写setApplicationContext()方法把名字和具体的策略绑定在一起

6.调用

6.1Controller层调用

   /**
     * 登录功能
     * @param loginFrom 登录参数,包含手机号、验证码;或者手机号、密码
     */
   
 @PostMapping(value = "/login")
    public Result loginByphone(@RequestBody LoginFromDTO loginFrom) {
//        log.debug("user/login:loginFromDTO=" + loginFrom);
        //返回的是token值
        return userService.login(loginFrom);
    }

6.2Service层

    @Override
    public Result login(LoginFromDTO loginFrom) {

        //策略模式实现的登录方式
        UserGranter granter = userLoginFactory.getGranter(loginFrom.getTypes());
        return granter.login(loginFrom);

    }

loginFrom.getTypes()这个方法获取的是前端传的参数,参数值必须是yml文件中左边的

6.2实体类

package com.example.barterbuddy.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
*<p> </p>
*接受前端登录信息
*@author panwu
*@descripion
*@date 2023/8/14 10:30
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginFromDTO {
    //手机号
    private String phone;
    //密码
    private String password;
    //邮箱号
    private String email;
    //验证码
    private String code;
    private String address;
    //登录类型,短信登录:smsGranter,密码登录:accountGranter等
    private  String types;
}

以上就是整个Demo的实现了,各位仔细阅读=.=

SpringAop+自定义注解+自定义异常实现接口权限校验

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值