美文网首页
七、微服务之用户服务

七、微服务之用户服务

作者: 薛定谔的猫_1406 | 来源:发表于2018-06-19 21:56 被阅读0次

    一、用户服务基于JWT的token认证实现

    传统的session身份认证
    • 缺点:session存储在内存中,这样就不能跨实例共享,当下一次请求分发到另外的实例中,就要重新登陆;
    • session是依赖于浏览器的cookie,当移动端访问的时候就很难支持。


      基于token的认证
    • 优点:1保证了服务的无状态,因为用户信息都是存在分布式缓存中;
    • 2 .不依赖于token机制,可以根据与客户端约定的协议来传输token。


      基于JWT的身份认证(JSON WEB TOKENS)
      JWT身份认证

    二、实现

    2.1 引入jwt的依赖

    <dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java.jwt</artifactId>
    </denpendency>
    

    2.2 定义JwtHelper,来生成token

    package com.mooc.house.user.utils;
    
    import java.io.UnsupportedEncodingException;
    import java.util.Date;
    import java.util.Map;
    
    import org.apache.commons.lang3.time.DateUtils;
    
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTCreator;
    import com.auth0.jwt.JWTVerifier;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.interfaces.Claim;
    import com.auth0.jwt.interfaces.DecodedJWT;
    import com.google.common.collect.Maps;
    
    public class JwtHelper {
      
      private static final String  SECRET = "session_secret";
      
      private static final String  ISSUER = "mooc_user";
      
      /**
      * 生成token的方法
      *@Param claims 表示将用户的哪些数据设置到token里,比如用户的姓名或者email。这样减少了数据库的压力,从token里获取用户信息
    */
      public static String genToken(Map<String, String> claims){
        try {
          //定义算法
          Algorithm algorithm = Algorithm.HMAC256(SECRET);
          //设置发布者 、过期时间信息
          JWTCreator.Builder builder = JWT.create().withIssuer(ISSUER).withExpiresAt(DateUtils.addDays(new Date(), 1));
        //将用户信息设置到token中
          claims.forEach((k,v) -> builder.withClaim(k, v));
          return builder.sign(algorithm).toString();
        } catch (IllegalArgumentException | UnsupportedEncodingException e) {
          throw new RuntimeException(e);
        }
      }
      
      /**
      * 校验操作
    */
      public static Map<String, String> verifyToken(String token)  {
        Algorithm algorithm = null;
        try {
          algorithm = Algorithm.HMAC256(SECRET);
        } catch (IllegalArgumentException | UnsupportedEncodingException e) {
          throw new RuntimeException(e);
        }
        JWTVerifier verifier = JWT.require(algorithm).withIssuer(ISSUER).build();
        DecodedJWT jwt =  verifier.verify(token);
        Map<String, Claim> map = jwt.getClaims();
        Map<String, String> resultMap = Maps.newHashMap();
        map.forEach((k,v) -> resultMap.put(k, v.asString()));
        return resultMap;
      }
    
    }
    
    

    2.3 service层操作

    vpackage com.mooc.house.user.service;
    
    import java.time.Instant;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.TimeUnit;
    
    import org.apache.commons.lang.RandomStringUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.alibaba.fastjson.JSON;
    import com.google.common.base.Strings;
    import com.google.common.collect.ImmutableMap;
    import com.mooc.house.user.common.UserException;
    import com.mooc.house.user.common.UserException.Type;
    import com.mooc.house.user.mapper.UserMapper;
    import com.mooc.house.user.model.User;
    import com.mooc.house.user.utils.BeanHelper;
    import com.mooc.house.user.utils.HashUtils;
    import com.mooc.house.user.utils.JwtHelper;
    
    @Service
    public class UserService {
    
      @Autowired
      private StringRedisTemplate redisTemplate;
      
      @Autowired
      private UserMapper userMapper;
      
      @Autowired
      private MailService mailService;
      
    
      @Value("${file.prefix}")
      private String imgPrefix;
      
      /**
       * 1.首先通过缓存获取
       * 2.不存在将从通过数据库获取用户对象
       * 3.将用户对象写入缓存,设置缓存时间5分钟
       * 4.返回对象
       * @param id
       * @return
       */
      public User getUserById(Long id) {
        String key = "user:"+id;
        String json =  redisTemplate.opsForValue().get(key);
        User user = null;
        if (Strings.isNullOrEmpty(json)) {
          user =  userMapper.selectById(id);
          user.setAvatar(imgPrefix + user.getAvatar());
          String string  = JSON.toJSONString(user);
          redisTemplate.opsForValue().set(key, string);
          redisTemplate.expire(key, 5, TimeUnit.MINUTES);
        }else {
          user = JSON.parseObject(json,User.class);
        }
        return user;
      }
    
      public List<User> getUserByQuery(User user) {
        List<User> users = userMapper.select(user);
        users.forEach(u -> {
          u.setAvatar(imgPrefix + u.getAvatar());
        });
        return users;
      }
    
      /**
       * 注册
       * @param user
       * @param enableUrl
       * @return
       */
      public boolean addAccount(User user, String enableUrl) {
        user.setPasswd(HashUtils.encryPassword(user.getPasswd()));
        BeanHelper.onInsert(user);
        userMapper.insert(user);
        registerNotify(user.getEmail(),enableUrl);
        return true;
      }
    
      /**
       * 发送注册激活邮件
       * @param email
       * @param enableUrl
       */
      private void registerNotify(String email, String enableUrl) {
        String randomKey = HashUtils.hashString(email) + RandomStringUtils.randomAlphabetic(10);
        redisTemplate.opsForValue().set(randomKey, email);
        redisTemplate.expire(randomKey, 1,TimeUnit.HOURS);
        String content = enableUrl +"?key="+  randomKey;
        mailService.sendSimpleMail("房产平台激活邮件", content, email);
      }
    
      public boolean enable(String key) {
        String email = redisTemplate.opsForValue().get(key);
        if (StringUtils.isBlank(email)) {
           throw new UserException(UserException.Type.USER_NOT_FOUND, "无效的key");
        }
        User updateUser = new User();
        updateUser.setEmail(email);
        updateUser.setEnable(1);
        userMapper.update(updateUser);
        return true;
      }
    
      /**
       * 校验用户名密码、生成token并返回用户对象
       * @param email
       * @param passwd
       * @return
       */
      public User auth(String email, String passwd) {
        if (StringUtils.isBlank(email) || StringUtils.isBlank(passwd)) {
          throw new UserException(Type.USER_AUTH_FAIL,"User Auth Fail");
        }
        User user = new User();
        user.setEmail(email);
        user.setPasswd(HashUtils.encryPassword(passwd));
        user.setEnable(1);
        List<User> list =  getUserByQuery(user);
        if (!list.isEmpty()) {
           User retUser = list.get(0);
           onLogin(retUser);
           return retUser;
        }
        throw new UserException(Type.USER_AUTH_FAIL,"User Auth Fail");
      }
    
      private void onLogin(User user) {
        String token =  JwtHelper.genToken(ImmutableMap.of("email", user.getEmail(), "name", user.getName(),"ts",Instant.now().getEpochSecond()+""));
        renewToken(token,user.getEmail());
        user.setToken(token);
      }
    
      private String renewToken(String token, String email) {
        redisTemplate.opsForValue().set(email, token);
        redisTemplate.expire(email, 30, TimeUnit.MINUTES);
        return token; 
      }
    
      public User getLoginedUserByToken(String token) {
        Map<String, String> map = null;
        try {
          map = JwtHelper.verifyToken(token);
        } catch (Exception e) {
          throw new UserException(Type.USER_NOT_LOGIN,"User not login");
        }
        String email =  map.get("email");
        Long expired = redisTemplate.getExpire(email);
        if (expired > 0L) {
          renewToken(token, email);
          User user = getUserByEmail(email);
          user.setToken(token);
          return user;
        }
        throw new UserException(Type.USER_NOT_LOGIN,"user not login");
        
      }
    
      private User getUserByEmail(String email) {
        User user = new User();
        user.setEmail(email);
        List<User> list = getUserByQuery(user);
        if (!list.isEmpty()) {
          return list.get(0);
        }
        throw new UserException(Type.USER_NOT_FOUND,"User not found for " + email);
      }
    
      public void invalidate(String token) {
        Map<String, String> map = JwtHelper.verifyToken(token);
        redisTemplate.delete(map.get("email"));
      }
    
      @Transactional(rollbackFor = Exception.class)
      public User updateUser(User user) {
         if (user.getEmail() == null) {
            return null;
         }
         if (!Strings.isNullOrEmpty(user.getPasswd()) ) {
            user.setPasswd(HashUtils.encryPassword(user.getPasswd()));
         }
         userMapper.update(user);
         return userMapper.selectByEmail(user.getEmail());
      }
    
      public void resetNotify(String email,String url) {
        String randomKey = "reset_" + RandomStringUtils.randomAlphabetic(10);
        redisTemplate.opsForValue().set(randomKey, email);
        redisTemplate.expire(randomKey, 1,TimeUnit.HOURS);
        String content = url +"?key="+  randomKey;
        mailService.sendSimpleMail("房产平台重置密码邮件", content, email);
        
      }
    
      public String getResetKeyEmail(String key) {
        return  redisTemplate.opsForValue().get(key);
      }
    
      public User reset(String key, String password) {
        String email = getResetKeyEmail(key);
        User updateUser = new User();
        updateUser.setEmail(email);
        updateUser.setPasswd(HashUtils.encryPassword(password));
        userMapper.update(updateUser);
        return getUserByEmail(email);
      }
    
    }
    
    登陆验证
    鉴权
    登出

    2.4 jwt的优势

    jwt的优势

    2.5 jwt的缺点

    jwt的缺点

    相关文章

      网友评论

          本文标题:七、微服务之用户服务

          本文链接:https://www.haomeiwen.com/subject/tjszeftx.html