用户密码登录接口和Token

本文详细介绍了一个基于微服务架构的用户密码登录接口实现过程,包括使用Ribbon进行客户端负载均衡,通过dm-user-provider和dm-user-consumer服务交互,利用MyBatis执行SQL查询验证用户信息,生成并缓存Token,最终实现用户登录功能。

用户密码登录接口

集群:多台机子同时部署一个服务

客户端负载均衡:Ribbon

1.物料:
(1)"用户登录页面.md"接口文档
(2)用户微服务工程(dm-user-provider,dm-user-consumer)
(2)生成token机制原理
(3)缓存用户信息和token信息
(4)修改数据库脚本:

SELECT * FROM dm_user WHERE phone= 'cj88sdj@163.com' AND PASSWORD = 'e10adc3949ba59abbe56e057f20f883e'

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O9UG8Dcj-1573225333163)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1573140660109.png)]

2.实现用户密码登录接口
思路1:使用到dm-user-provider,dm-user-consumer,dm-common
思路2:redisUtils.get(tokenKey)方法代表的含义:判断redis中是否存在该对象

步骤1:
dm-common/dm-common-dao/mapper/DmUserMapper接口

public DmUser checkLoginByPassword(@Param(value = "phone") String phone, @Param(value = "password") String password) throws Exception;

dm-common/dm-common-dao/resources/DmUserMapper.xml

<select id="checkLoginByPassword" resultType="cn.dm.pojo.DmUser">
		select
		    id as id,
		    phone as phone,
		    wxUserId as wxUserId,
		    realName as realName,
		    nickName as nickName,
		    sex as sex,
		    idCard as idCard,
		    birthday as birthday,
		    createdTime as createdTime,
		    updatedTime as updatedTime,
		    hobby as hobby
		from dm_user
		where phone=#{phone} and password=#{password}
</select>

步骤2:dm-common/dm-common-client/RestDmUserClient
(1)加入:

@RequestMapping(value = "/checkLoginByPassword", method = RequestMethod.POST)
public DmUser checkLoginByPassword(@RequestBody DmUser dmUser) throws Exception;

@RequestMapping(value = "/generateToken", method = RequestMethod.POST)
public String generateToken(@RequestBody DmUser dmUser) throws Exception;

(2)实现:DmUserClientFallBack客户端代码

(3)本地构建

步骤3:
dm-user-provider/RestDmUserService

@RestController
public class RestDmUserService {
@Autowired
private DmUserMapper dmUserMapper;

@RequestMapping(value = "/checkLoginByPassword",method = RequestMethod.POST)
public DmUser checkLoginByPassword(@RequestBody DmUser dmUser)throws Exception{
	return dmUserMapper.checkLoginByPassword(dmUser.getPhone(),dmUser.getPassword());
}
}

dm-user-provider/RestDmTokenService

@RestController
public class RestDmTokenService {
    private String tokenPrefix = "token";

    @RequestMapping(value = "/generateToken", method = RequestMethod.POST)
    public String generateToken(@RequestBody DmUser dmUser) {
        StringBuilder sb = new StringBuilder();
        sb.append(tokenPrefix + "-");
        sb.append("PC-" + "-");
        String info = MD5.getMd5(EmptyUtils.isEmpty(dmUser.getPhone()) ? dmUser.getWxUserId() : dmUser.getPhone(), 32);
        sb.append(info + "-");
        sb.append(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "-");
        sb.append(UUID.randomUUID().toString().substring(0, 6));
        return sb.toString();
    }
}

步骤4:创建dm-user-consumer/DmUserController

@RestController
@RequestMapping("/api")
public class DmUserController {

    @Autowired
    private DmLoginService dmLoginService;

    @RequestMapping(value = "/p/login",method = RequestMethod.POST)
    public Dto loginByPassword(@RequestBody DmUser dmUser) throws Exception {
        dmUser.setPassword(MD5.getMd5(dmUser.getPassword(),32));//32为加“盐”
        Object[] results = dmLoginService.login(dmUser);
        if (EmptyUtils.isEmpty(results)) {
            return DtoUtil.returnFail("登陆失败","0000");
        }else {
            return DtoUtil.returnSuccess("登陆成功",results);
        }
    }
}

步骤五:dm-user-consumer/DmLoginService

/**
  * 账号密码登陆
  *
  * @param dmUser
  * @return
  * @throws Exception
  */
  public Object[] login(DmUser dmUser) throws Exception;

步骤六:dm-user-consumer/DmLoginServiceImpl

@Component
public class DmLoginServiceImpl implements DmLoginService {
    @Autowired
    private RestDmUserClient restDmUserClient;

    @Autowired
    private RestDmImageClient restDmImageClient;

    @Autowired
    private RedisUtils redisUtils;


    @Override
    public Object[] login(DmUser user) throws Exception {


        //1.通过用户名和密码两个参数到数据库里获取用户对象,并进行校验
        DmUser dmUser = restDmUserClient.checkLoginByPassword(user);
        //2.判空校验
        if (EmptyUtils.isEmpty(dmUser)) {
            return null;
        }
        //3.定义dmUserVO,并组装
        DmUserVO dmUserVO = new DmUserVO();

        //4.将数据库中的用户对象复制到dmUserVO中

        BeanUtils.copyProperties(dmUser, dmUserVO);

        //5.更新用户id
        dmUserVO.setUserId(dmUser.getId());


        //6.通过用户id查询图片库对象
        List<DmImage> dmImageList = restDmImageClient.queryDmImageList(dmUser.getId(),
                Constants.Image.ImageType.normal,
                Constants.Image.ImageCategory.user);

        //7.判空校验,并更新图片id和图片URL
        if (EmptyUtils.isNotEmpty(dmImageList)) {
            dmUserVO.setImageId(dmImageList.get(0).getId());
            dmUserVO.setImgUrl(dmImageList.get(0).getImgUrl());
        }

        //8.调用restDmUserClient中的generateToken()方法,生成token
        String token = this.generateToken(dmUser);

        //9.(重要!)将token和用户对象保存在redis中
        this.saveToken(token, dmUserVO);

        //10.组装Vo
        TokenVO tokenVO = new TokenVO(token, Constants.Redis_Expire.SESSION_TIMEOUT, System.currentTimeMillis());

        //11.返回用户VO,tokenVO
        return new Object[]{dmUserVO, tokenVO};
    }

    private String generateToken(DmUser dmUser)throws Exception {
        return restDmUserClient.generateToken(dmUser);
    }


    /**
     * (置换token)
     *  将token和用户对象保存在redis中
     */
    private void saveToken(String token, DmUserVO dmUserVO) {
        String tokenKey = Constants.USER_TOKEN_PREFIX + dmUserVO.getUserId();
        String tokenValue = null;


        //1.判断如果redis中包含token,则删除原有的token
        if ((tokenValue = (String) redisUtils.get(tokenKey)) != null) {
            redisUtils.delete(tokenKey);
        }


        //2.重新在redis中生成key为tokenKey,value为token的值
        redisUtils.set(tokenKey, Constants.Redis_Expire.SESSION_TIMEOUT, token);

        //3.在redis中塞入key为token,value为用户对象的值。
        redisUtils.set(token, Constants.Redis_Expire.SESSION_TIMEOUT, JSON.toJSONString(dmUserVO));
    }
}

打开Redis
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bWCDTgpY-1573225333164)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1573223104221.png)]

测试:
http://localhost:7100/api/p/login

设置:
headers:Content-Type application/json

{
"phone":"cj88sdj@163.com",
"password":"123456"
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d2lnLVCP-1573225333165)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1573223327871.png)]

此时,已经生成了token

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KxvQG48v-1573225333165)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1573223422648.png)]

(userToken:)文件夹的形式保存 (token为value)

//在redis中生成两对键值对
U_id:token
token:user
//但凡redis存储池的时候,只要加个冒号,它就以文件夹的形式显示在redis当中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M8ANLDaV-1573225333166)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1573223867077.png)]

此处为PC端生成的token(以token为key找到对象)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lh7q2hKs-1573225333167)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1573224294298.png)]
失效时间TTL,若超过失效时间,需要重新登录,置换token

Spring实现用户账号密码登录并获取token可通过Spring SecuritySpring OAuth2实现。 ### 实现思路 1. **引入依赖**:在项目的`pom.xml`文件中添加Spring SecuritySpring OAuth2相关依赖。以Spring Security为例,添加以下依赖: ```xml <!-- Spring Security依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 2. **配置安全框架**:利用Security框架实现用户登录验证token用户授权。其实现逻辑为:输入用户密码,提交后将用户密码封装对象,调用方法实现验证,根据用户名查询用户信息,查询用户信息返回对象,进行密码比较,填充并返回,最后将返回对象放到上下文对象里面 [^3]。 3. **编写登录接口**:在登录接口中,通过`AuthenticationManager`的`authenticate`方法来进行用户认证。示例代码如下: ```java @Autowired RedisTemplate redisTemplate; @Autowired AuthenticationManager authenticationManager; @PostMapping("/user/login") public Result login(@RequestBody TbUser tbUser){ //通过AuthenticationManager的authenticate方法来进行用户认证 UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(tbUser.getUsername(),tbUser.getPassword()); Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken); if(authenticate==null){ return Result.error("401","登录校验失败"); } else { //获取用户信息 MyUser myUser = (MyUser) authenticate.getPrincipal(); //获取用户id Long id = myUser.getTbUser().getId(); //根据用户id生成token String token = JwtUtil.generateToken(id); //将token放在数据库中 redisTemplate.opsForValue().set(String.valueOf(id),myUser,30, TimeUnit.MINUTES); return Result.success(token); } } ``` 在上述代码中,若认证成功,会根据用户id生成token,并将token存于数据库,最后返回token给客户端;若认证失败,则返回登录校验失败的提示 [^2]。 ### 其他说明 Spring OAuth2提供了授权码、密码等模式,可实现用户密码登录、手机号验证码登录并返回token。在app中,可实现用户密码或者手机号验证码登录成功之后返回token的功能 [^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值