• 7.【Spring Cloud Alibaba】微服务的用户认证与授权


    有状态 vs 无状态

    有状态

    image

    那么Session在何时创建呢?
    当然还是在服务器端程序运行的过程中创建的,不同语言实现的应用程序有不同创建Session的方法,而在Java中是通过调用HttpServletRequest的getSession方法(使用true作为参数)创建的。
    在创建了Session的同时,服务器会为该Session生成唯一的Session id,而这个Session id在随后的请求中会被用来重新获得已经创建的Session;
    在Session被创建之后,就可以调用Session相关的方法往Session中增加内容了,而这些内容只会保存在服务器中,发到客户端的只有Session id;
    当客户端再次发送请求的时候,会将这个Session id带上,服务器接受到请求之后就会依据Session id找到相应的Session,从而再次使用之。
    

    无状态

    image

    有状态 vs 无状态

    image

    微服务认证方案

    • 微服务认证方案01-处处安全
    • 微服务认证方案02-外部无状态,内部有状态
    • 微服务认证方案03-网关认证授权,内部裸奔
    • 微服务认证方案04-内部裸奔改进

    微服务认证方案01-处处安全

    OAuth2实现单点登录SSO

    https://github.com/chengjiansheng/cjs-oauth2-sso-demo.git

    image

    image

    微服务认证方案02-外部无状态,内部有状态

    image

    image

    微服务认证方案03-网关认证授权,内部裸奔

    image

    微服务认证方案04-内部裸奔改进

    image

    方案对比与选择

    image

    访问控制模型

    image

    image

    JWT是什么?

    image

    image

    image

    JWT操作工具类分享

    小程序登录逻辑

    image

    实现登录状态检查

    image

    public @interface CheckLogin {
    }
    
    
    import com.itmuch.usercenter.util.JwtOperator;
    import io.jsonwebtoken.Claims;
    import lombok.RequiredArgsConstructor;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestAttributes;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    import java.lang.reflect.Method;
    import java.util.Objects;
    
    @Aspect
    @Component
    @RequiredArgsConstructor(onConstructor = @__(@Autowired))
    public class AuthAspect {
        private final JwtOperator jwtOperator;
    
        @Around("@annotation(com.itmuch.usercenter.auth.CheckLogin)")
        public Object checkLogin(ProceedingJoinPoint point) throws Throwable {
            checkToken();
            return point.proceed();
        }
    
        private void checkToken() {
            try {
                // 1. 从header里面获取token
                HttpServletRequest request = getHttpServletRequest();
    
                String token = request.getHeader("X-Token");
    
                // 2. 校验token是否合法&是否过期;如果不合法或已过期直接抛异常;如果合法放行
                Boolean isValid = jwtOperator.validateToken(token);
                if (!isValid) {
                    throw new SecurityException("Token不合法!");
                }
    
                // 3. 如果校验成功,那么就将用户的信息设置到request的attribute里面
                Claims claims = jwtOperator.getClaimsFromToken(token);
                request.setAttribute("id", claims.get("id"));
                request.setAttribute("wxNickname", claims.get("wxNickname"));
                request.setAttribute("role", claims.get("role"));
            } catch (Throwable throwable) {
                throw new SecurityException("Token不合法");
            }
        }
    
        private HttpServletRequest getHttpServletRequest() {
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            return attributes.getRequest();
        }
    
        @Around("@annotation(com.itmuch.usercenter.auth.CheckAuthorization)")
        public Object checkAuthorization(ProceedingJoinPoint point) throws Throwable {
            try {
                // 1. 验证token是否合法;
                this.checkToken();
                // 2. 验证用户角色是否匹配
                HttpServletRequest request = getHttpServletRequest();
                String role = (String) request.getAttribute("role");
    
                MethodSignature signature = (MethodSignature) point.getSignature();
                Method method = signature.getMethod();
                CheckAuthorization annotation = method.getAnnotation(CheckAuthorization.class);
    
                String value = annotation.value();
    
                if (!Objects.equals(role, value)) {
                    throw new SecurityException("用户无权访问!");
                }
            } catch (Throwable throwable) {
                throw new SecurityException("用户无权访问!", throwable);
            }
            return point.proceed();
        }
    }
    
    @GetMapping("/{id}")
    @CheckLogin
    public User findById(@PathVariable Integer id) {
        log.info("我被请求了...");
        return this.userService.findById(id);
    }
    

    Feign调用时传递token

    使用拦截器

    import feign.RequestInterceptor;
    import feign.RequestTemplate;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.web.context.request.RequestAttributes;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    
    public class TokenRelayRequestIntecepor implements RequestInterceptor {
        @Override
        public void apply(RequestTemplate template) {
            // 1. 获取到token
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = attributes.getRequest();
            String token = request.getHeader("X-Token");
    
            // 2. 将token传递
            if (StringUtils.isNotBlank(token)) {
                template.header("X-Token", token);
            }
    
        }
    }
    

    RestTemplate传递token

    image

    RequestInterceptor
    import feign.RequestInterceptor;
    import feign.RequestTemplate;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.web.context.request.RequestAttributes;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    
    public class TokenRelayRequestIntecepor implements RequestInterceptor {
        @Override
        public void apply(RequestTemplate template) {
            // 1. 获取到token
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = attributes.getRequest();
            String token = request.getHeader("X-Token");
    
            // 2. 将token传递
            if (StringUtils.isNotBlank(token)) {
                template.header("X-Token", token);
            }
    
        }
    }
    
    ClientHttpRequestInterceptor
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpRequest;
    import org.springframework.http.client.ClientHttpRequestExecution;
    import org.springframework.http.client.ClientHttpRequestInterceptor;
    import org.springframework.http.client.ClientHttpResponse;
    import org.springframework.web.context.request.RequestAttributes;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    public class TestRestTemplateTokenRelayInterceptor implements ClientHttpRequestInterceptor {
        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest httpRequest = attributes.getRequest();
            String token = httpRequest.getHeader("X-Token");
    
            HttpHeaders headers = request.getHeaders();
            headers.add("X-Token", token);
    
            // 保证请求继续执行
            return execution.execute(request, body);
        }
    }
    

    实现用户权限验证

    @Retention(RetentionPolicy.RUNTIME)
    public @interface CheckAuthorization {
        String value();
    }
    
    
    import com.itmuch.usercenter.util.JwtOperator;
    import io.jsonwebtoken.Claims;
    import lombok.RequiredArgsConstructor;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestAttributes;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    import java.lang.reflect.Method;
    import java.util.Objects;
    
    @Aspect
    @Component
    @RequiredArgsConstructor(onConstructor = @__(@Autowired))
    public class AuthAspect {
        private final JwtOperator jwtOperator;
    
        @Around("@annotation(com.itmuch.usercenter.auth.CheckLogin)")
        public Object checkLogin(ProceedingJoinPoint point) throws Throwable {
            checkToken();
            return point.proceed();
        }
    
        private void checkToken() {
            try {
                // 1. 从header里面获取token
                HttpServletRequest request = getHttpServletRequest();
    
                String token = request.getHeader("X-Token");
    
                // 2. 校验token是否合法&是否过期;如果不合法或已过期直接抛异常;如果合法放行
                Boolean isValid = jwtOperator.validateToken(token);
                if (!isValid) {
                    throw new SecurityException("Token不合法!");
                }
    
                // 3. 如果校验成功,那么就将用户的信息设置到request的attribute里面
                Claims claims = jwtOperator.getClaimsFromToken(token);
                request.setAttribute("id", claims.get("id"));
                request.setAttribute("wxNickname", claims.get("wxNickname"));
                request.setAttribute("role", claims.get("role"));
            } catch (Throwable throwable) {
                throw new SecurityException("Token不合法");
            }
        }
    
        private HttpServletRequest getHttpServletRequest() {
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            return attributes.getRequest();
        }
    
        @Around("@annotation(com.itmuch.usercenter.auth.CheckAuthorization)")
        public Object checkAuthorization(ProceedingJoinPoint point) throws Throwable {
            try {
                // 1. 验证token是否合法;
                this.checkToken();
                // 2. 验证用户角色是否匹配
                HttpServletRequest request = getHttpServletRequest();
                String role = (String) request.getAttribute("role");
    
                MethodSignature signature = (MethodSignature) point.getSignature();
                Method method = signature.getMethod();
                CheckAuthorization annotation = method.getAnnotation(CheckAuthorization.class);
    
                String value = annotation.value();
    
                if (!Objects.equals(role, value)) {
                    throw new SecurityException("用户无权访问!");
                }
            } catch (Throwable throwable) {
                throw new SecurityException("用户无权访问!", throwable);
            }
            return point.proceed();
        }
    }
    
    @PutMapping("/audit/{id}")
    @CheckAuthorization("admin")
    public Share auditById(@PathVariable Integer id, @RequestBody ShareAuditDTO auditDTO) {
        return this.shareService.auditById(id, auditDTO);
    }
    

    本章总结

    image

  • 相关阅读:
    [js开源组件开发]ajax分页组件
    使用nodejs+express+socketio+mysql搭建聊天室
    [js开源组件开发]图片放大镜
    JY游戏之毁经典《扫雷》
    JY游戏之手游《打卡乐猫》
    [js开源组件开发]图片懒加载lazyload
    [js开源组件开发]js文本框计数组件
    [js开源组件开发]js轮播图片支持手机滑动切换
    [js开源组件开发]js手机联动选择日期 开源git
    [js开源组件开发]-手机端照片预览组件
  • 原文地址:https://www.cnblogs.com/xjknight/p/12349113.html
Copyright © 2020-2023  润新知