zoukankan      html  css  js  c++  java
  • 046 用户登录功能02----前端代码和网关的登录拦截器

    1.首页判断登录状态

    虽然cookie已经成功写入,但是我们首页的顶部,登录状态依然没能判断出用户信息:

    这里需要向后台发起请求,根据cookie获取当前用户的信息

    (1)页面JS代码

    页面的顶部已经被我们封装为一个独立的Vue组件,在/js/pages/shortcut.js

    打开js,发现里面已经定义好了Vue组件,并且在created函数中,查询用户信息:

    查看网络控制台,发现发起了请求:

    因为token在cookie中,因此本次请求肯定会携带token信息在头中。

    (2)后台实现校验用户接口

    我们在leyou-auth-service中定义用户的校验接口,通过cookie获取token,然后校验通过返回用户信息。

    • 请求方式:GET

    • 请求路径:/verify

    • 请求参数:无,不过我们需要从cookie中获取token信息

    • 返回结果:UserInfo,校验成功返回用户信息;校验失败,则返回401

    代码:

    /**
         * 验证用户信息
         * @param token
         * @return
         */
        @GetMapping("verify")
        public ResponseEntity<UserInfo> verifyUser(@CookieValue("LY_TOKEN")String token){
            try {
                // 从token中解析token信息
                UserInfo userInfo = JwtUtils.getInfoFromToken(token, this.prop.getPublicKey());
                // 解析成功返回用户信息
                return ResponseEntity.ok(userInfo);
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 出现异常则,响应500
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }

    (3)测试

    重启leyou-auth-service微服务

    (4)刷新token

    每当用户在页面进行新的操作,都应该刷新token的过期时间,否则30分钟后用户的登录信息就无效了。而刷新其实就是重新生成一份token,然后写入cookie即可。

    那么问题来了:我们怎么知道用户有操作呢?

    事实上,每当用户来查询其个人信息,就证明他正在浏览网页,此时刷新cookie是比较合适的时机。因此我们可以对刚刚的校验用户登录状态的接口进行改进,加入刷新token的逻辑。

    /**
      * 验证用户信息
      * @param token
      * @return
      */
    @GetMapping("verify")
    public ResponseEntity<UserInfo> verifyUser(@CookieValue("LY_TOKEN")String token, HttpServletRequest request, HttpServletResponse response){
        try {
            // 从token中解析token信息
            UserInfo userInfo = JwtUtils.getInfoFromToken(token, this.properties.getPublicKey());
            // 解析成功要重新刷新token
            token = JwtUtils.generateToken(userInfo, this.properties.getPrivateKey(), this.properties.getExpire());
            // 更新cookie中的token
            CookieUtils.setCookie(request, response, this.properties.getCookieName(), token, this.properties.getCookieMaxAge());
    
            // 解析成功返回用户信息
            return ResponseEntity.ok(userInfo);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 出现异常则,响应500
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }

    2.网关的登录拦截器

    接下来,我们在Zuul编写拦截器,对用户的token进行校验,如果发现未登录,则进行拦截。

    (1)引入jwt相关配置

    既然是登录拦截,一定是前置拦截器,我们在leyou-gateway中定义

    首先在pom.xml中,引入所需要的依赖:

    <dependency>
                <groupId>lucky.leyou.auth</groupId>
                <artifactId>leyou-auth-common</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
            <dependency>
                <groupId>lucky.leyou.common</groupId>
                <artifactId>leyou-common</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>

    然后编写application.yml属性文件,添加如下内容:

    leyou:
      jwt:
        pubKeyPath: D:	emp
    sa\rsa.pub # 公钥地址
        cookieName: LY_TOKEN #cookie的名称

    编写属性类,读取公钥:

    package lucky.leyou.config;
    
    import lucky.leyou.auth.utils.RsaUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    import javax.annotation.PostConstruct;
    import java.security.PublicKey;
    
    @ConfigurationProperties(prefix = "leyou.jwt")
    public class JwtProperties {
    
        private String pubKeyPath;// 公钥
    
        private PublicKey publicKey; // 公钥
    
        private String cookieName;
    
        private static final Logger logger = LoggerFactory.getLogger(JwtProperties.class);
    
        @PostConstruct
        public void init(){
            try {
                // 获取公钥和私钥
                this.publicKey = RsaUtils.getPublicKey(pubKeyPath);
            } catch (Exception e) {
                logger.error("初始化公钥失败!", e);
                throw new RuntimeException();
            }
        }
    
        public String getPubKeyPath() {
            return pubKeyPath;
        }
    
        public void setPubKeyPath(String pubKeyPath) {
            this.pubKeyPath = pubKeyPath;
        }
    
        public PublicKey getPublicKey() {
            return publicKey;
        }
    
        public void setPublicKey(PublicKey publicKey) {
            this.publicKey = publicKey;
        }
    
        public String getCookieName() {
            return cookieName;
        }
    
        public void setCookieName(String cookieName) {
            this.cookieName = cookieName;
        }
    }

    (2)编写过滤器逻辑

    基本逻辑:

    • 获取cookie中的token

    • 通过JWT对token进行校验

    • 通过:则放行;不通过:则重定向到登录页

    package lucky.leyou.filter;
    
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import lucky.leyou.auth.utils.JwtUtils;
    import lucky.leyou.common.utils.CookieUtils;
    import lucky.leyou.config.JwtProperties;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Component
    @EnableConfigurationProperties(JwtProperties.class)
    public class LoginFilter extends ZuulFilter {
    
        @Autowired
        private JwtProperties properties;
    
        @Override
        public String filterType() {
            return "pre";
        }
    
        @Override
        public int filterOrder() {
            return 5;
        }
    
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        @Override
        public Object run() throws ZuulException {
            // 获取上下文
            RequestContext context = RequestContext.getCurrentContext();
            // 获取request
            HttpServletRequest request = context.getRequest();
            // 获取token
            String token = CookieUtils.getCookieValue(request, this.properties.getCookieName());
            // 校验
            try {
                // 校验通过什么都不做,即放行
                JwtUtils.getInfoFromToken(token, this.properties.getPublicKey());
            } catch (Exception e) {
                // 校验出现异常,返回403
                context.setSendZuulResponse(false);
                context.setResponseStatusCode(HttpStatus.FORBIDDEN.value());
            }
            return null;
        }
    }

    重启,刷新页面,发现请求校验的接口也被拦截了:

    证明我们的拦截器生效了,但是,这个路径似乎不应该被拦截啊!

    (3)白名单

    要注意,并不是所有的路径我们都需要拦截,例如:

    • 登录校验接口:/auth/**

    • 注册接口:/user/register

    • 数据校验接口:/user/check/**

    • 发送验证码接口:/user/code

    • 搜索接口:/search/**

    另外,跟后台管理相关的接口,因为我们没有做登录和权限,因此暂时都放行,但是生产环境中要做登录校验:

    • 后台商品服务:/item/**

    所以,我们需要在拦截时,配置一个白名单,如果在名单内,则不进行拦截。

    application.yaml中添加规则:

    leyou:
      filter:
        allowPaths:
          - /api/auth
          - /api/search
          - /api/user/register
          - /api/user/check
          - /api/user/code
          - /api/item

    然后读取这些属性:

    package lucky.leyou.config;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    import java.util.List;
    
    @ConfigurationProperties(prefix = "leyou.filter")
    public class FilterProperties {
    
        private List<String> allowPaths;
    
        public List<String> getAllowPaths() {
            return allowPaths;
        }
    
        public void setAllowPaths(List<String> allowPaths) {
            this.allowPaths = allowPaths;
        }
    }

    在过滤器中的shouldFilter方法中添加判断逻辑

    代码:

    package lucky.leyou.filter;
    
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import lucky.leyou.auth.utils.JwtUtils;
    import lucky.leyou.common.utils.CookieUtils;
    import lucky.leyou.config.FilterProperties;
    import lucky.leyou.config.JwtProperties;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * 拦截过滤器
     */
    @Component
    @EnableConfigurationProperties({JwtProperties.class, FilterProperties.class})
    public class LoginFilter extends ZuulFilter {
    
        @Autowired
        private JwtProperties properties;
    
        @Autowired
        private FilterProperties filterProp;
    
        @Override
        public String filterType() {
            return "pre";
        }
    
        @Override
        public int filterOrder() {
            return 5;
        }
    
    
        @Override
        public Object run() throws ZuulException {
            // 获取上下文
            RequestContext context = RequestContext.getCurrentContext();
            // 获取request
            HttpServletRequest request = context.getRequest();
            // 获取token
            String token = CookieUtils.getCookieValue(request, this.properties.getCookieName());
            // 校验
            try {
                // 校验通过什么都不做,即放行
                JwtUtils.getInfoFromToken(token, this.properties.getPublicKey());
            } catch (Exception e) {
                // 校验出现异常,返回403
                context.setSendZuulResponse(false);
                context.setResponseStatusCode(HttpStatus.FORBIDDEN.value());
            }
            return null;
        }
    
        @Override
        public boolean shouldFilter() {
            // 获取上下文
            RequestContext ctx = RequestContext.getCurrentContext();
            // 获取request
            HttpServletRequest req = ctx.getRequest();
            // 获取路径
            String requestURI = req.getRequestURI();
            // 判断白名单
            // 遍历允许访问的路径
            for (String path : this.filterProp.getAllowPaths()) {
                // 然后判断是否是符合
                if(requestURI.startsWith(path)){
                    return false;
                }
            }
            return true;
        }
    }

    再次测试:

     

     

  • 相关阅读:
    懒加载——实现原理
    html5shiv.js和respond.min.js
    点击app分享链接,js判断手机是否安装某款app,有就尝试打开,没有就下载
    ajax获取后台数据渲染(整片文章不分段落)解决方案,要使用htmL方式输出
    +-下拉菜单
    html 中a标签的问题(无反应,跳转,调用方法)
    js中两种定时器,setTimeout和setInterval的区别
    chrome 调试进入 paused in debugger 状态解决办法
    mybatis-plus 获取新增id
    linux unzip和zip
  • 原文地址:https://www.cnblogs.com/luckyplj/p/11630288.html
Copyright © 2011-2022 走看看