健康管理系统花絮(4)

  • 第四课主要是基于 redis 实现单点登录。早期互联网应用由于用户少、访问量低,多个系统的代码可以部署在一台机器中,所以 session 可以直接共享。但是随着互联网的发展,现如今大型网站均是分布式系统,session 无法再像以前那样共享。
  • 单点登录就是在这样多系统共存的环境下,实现一次登录,多次访问不同的互信的系统,单点登录可以简化用户操作,提高友好性。单点登录可以有多种实现方式,本实验采用 token+ redis 方式实现。在用户第一次访问后端接口时直接跳到登录页面,使用账号、密码验证用户真实性,验证成功后,后端返回给用户随机产生的 token(存储在 redis 中)。随后,用户访问后端的接口都要携带这个 token,使用 token 来验证用户的有效性。用户注销时,token 从redis 中删除。

文件夹结构图.png

RedisConfig.java

// 标识configuration
@Configuration
public class RedisConfig {

    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        template.afterPropertiesSet();

        return template;
    }

}

WebConfig.java

// 标识configuration
@Configuration
public class WebConfig extends WebMvcConfigurationSupport{

    // 自动注入拦截器
    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor).addPathPatterns("/user/**").excludePathPatterns("/user/login");

        super.addInterceptors(registry);
    }
}

UserController.java

// 标记controller,返回json数据,URL前缀:/user
@RestController
@RequestMapping("user")
public class UserController {

    // 自动注入service
    @Autowired
    private UserService userService;

    /**
     * 登录
     * @param user
     * @return
     */
    @PostMapping("login")
    public ResultObject login(@RequestBody User user) {
        if (user == null) {
            return new ResultObject(-1, "param empty", null);
        }
        ResultObject resultObject = userService.login(user);
        return resultObject;
    }

    /**
     * 测试单点登录
     * @param
     * @return
     */
    @GetMapping("test")
    public ResultObject test() {
        ResultObject resultObject = new ResultObject(1, "test success", null);
        return resultObject;
    }

    /**
     * 注销
     * @param token
     * @return
     */
    @GetMapping("logout")
    public ResultObject logout(@RequestParam String token) {
        ResultObject resultObject = userService.logout(token);
        return resultObject;
    }

}

UserMapper.java

@Mapper
public interface UserMapper {

    /**
     * 查询用户
     * @param user
     * @return
     */
    @Select("select id, name, gender, age, password from user " +
            " where name = #{name} and password = #{password}")
    User select(User user);
}

ResultObject.java

public class ResultObject {

    // 后台状态
    private int code;
    // 相关消息
    private String msg;
    // 后台返回数据
    private Object result;

    // 构造函数
    public ResultObject() {
        super();
        // TODO Auto-generated constructor stub
    }
    // 构造函数
    public ResultObject(int code, String msg, Object result) {
        super();
        this.code = code;
        this.msg = msg;
        this.result = result;
    }

    // 属性setter、getter方法
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public Object getResult() {
        return result;
    }
    public void setResult(Object result) {
        this.result = result;
    }

    // 重写toString方法
    @Override
    public String toString() {
        return "ResultObject [code=" + code + ", msg=" + msg + ", result=" + result + "]";
    }
}

User.java

@JsonInclude(JsonInclude.Include.NON_DEFAULT)
public class User implements Serializable {

    private static final long serialVersionUID = 1L;
    // 主键自增id
    private int id;
    // 姓名
    private String name;
    // 性别
    private int gender;
    // 年龄
    private int age;
    // 密码
    private String password;

    // 构造函数
    public User() {
        super();
        // TODO Auto-generated constructor stub
    }
    // 构造函数
    public User(String name, int gender, int age, String password) {
        super();
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.password = password;
    }

    // 属性setter、getter方法
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getGender() {
        return gender;
    }

    public void setGender(int gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    // 重写toString方法
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", gender=" + gender + ", age=" + age + ", password=" + password + "]";
    }
}

LoginInterceptor.java

@Component
public class LoginInterceptor  implements HandlerInterceptor{

    private static final Log logger = LogFactory.getLog(LoginInterceptor.class);

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        try {
            // 校验用户是否已经登录, 如果登录过, 将之前用户踢掉, 同时更新缓存中用户信息
            // 获取用户id
            // step 1 检查请求是否携带token

            String token = request.getParameter("token");
            if (token == null) {
                render(response, "lack token");
                return false;
            }

            // step 2  检查token是否合法
            User user = (User)redisUtil.get(token);
            if (user != null) {
                Object preToken = redisUtil.get(Integer.toString(user.getId()));
                // step 3  检查同一账号是否被其他用户登录
                if (!token.equals(preToken)) {
                    render(response, "another user login");
                    return false;
                    // step 4  至此登录成功,查看token过期时间是否小于1分钟, 返回值单位毫秒,故 * 1000比较
                } else {
                    Long expire = redisUtil.getExpire(token);
                    if (expire < 60 * 1000) {
                        redisUtil.expire(token, 60 * 30);
                    }
                }
            } else {
                render(response, "token error");
                return false;
            }
        } catch (Exception e) {
            logger.info("preHandle=" + e);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }

    public void render(HttpServletResponse response, String msg) throws IOException {
        ResultObject resultObject = new ResultObject(-1, msg, null);

        JSONObject object = new JSONObject(resultObject);

        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = response.getOutputStream();
        out.write(object.toString().getBytes("UTF-8"));
        out.flush();
        out.close();
    }
}

UserService.java

@Service
public class UserService {

    // 自动注入mapper
    @Autowired
    private UserMapper userMapper;

    // 自动注入redisUtil
    @Autowired
    private RedisUtil redisUtil;

    /**
     * 登录
     * @param user
     * @return
     */
    public ResultObject login(User user) {
        ResultObject resultObject = new ResultObject();

        // 查询用户账号、密码是否正确
        User fullUser = userMapper.select(user);
        // 不正确直接返回
        if (fullUser == null) {
            resultObject.setCode(-1);
            resultObject.setMsg("name or password error");
            return resultObject;
        }

        // 生成随机token
        String token = UUID.randomUUID().toString();
        int interval = 60 * 5;

        // 将用户的token作为key, 用户信息作为value
        redisUtil.set(token, fullUser, interval);
        // 防止同一个账号多人登录
        redisUtil.set(Integer.toString(fullUser.getId()), token, interval);

        resultObject.setCode(1);
        resultObject.setMsg("login success");
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);

        fullUser.setPassword(null);
        map.put("user", fullUser);
        resultObject.setResult(map);

        return resultObject;
    }

    /**
     * 注销
     * @param token
     * @return
     */
    public ResultObject logout(String token) {
        User user = (User)redisUtil.get(token);
        redisUtil.del(Integer.toString(user.getId()));
        redisUtil.del(token);
        return new ResultObject(1, "success", null);
    }

}

RedisUtil.java

@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 设置过期时间
     * @param key
     * @param time
     * @return
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取key的过期时间
     * @param key
     * @return
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 检测key是否存在
     * @param key
     * @return
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除key
     * @param key
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    /**
     * 获取key
     * @param key
     * @return
     */

    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 设置key
     * @param key
     * @param value
     * @return
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 设置带有过期时间的key,默认永不过期
     * @param key
     * @param value
     * @param time
     * @return
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * value + delta
     * @param key
     * @param delta
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("delta must > 0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * value - delta
     * @param key
     * @param delta
     * @return
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("delta must > 0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }
}

猜你喜欢

转载自blog.csdn.net/affluent6/article/details/91530021