微服务JWT安全密钥认证授权详解

JAVA微服务JWT安全密钥认证授权

微服务登录分为有状态以及无状态登录方法

有状态

有状态登录方法依赖Session,将登录状态信息放置在Session中,并使用一个Session Store存储

JWT%204ce37e5d266b4867b3bd93740fcb84e9/Untitled.png

无状态

服务器端不用去存储session状态,不会出现上述的负载均衡后服务器登录失效问题

JWT%204ce37e5d266b4867b3bd93740fcb84e9/Untitled%201.png

优缺点对比

JWT%204ce37e5d266b4867b3bd93740fcb84e9/Untitled%202.png

  1. 处处安全
  2. 外部无状态,内部有状态

JWT%204ce37e5d266b4867b3bd93740fcb84e9/Untitled%203.png

JWT%204ce37e5d266b4867b3bd93740fcb84e9/Untitled%204.png

  1. 网关认证授权,内部裸奔

JWT%204ce37e5d266b4867b3bd93740fcb84e9/Untitled%205.png

  1. 内部裸奔改进

JWT%204ce37e5d266b4867b3bd93740fcb84e9/Untitled%206.png

先在认证授权中心登录,登陆成功,颁发Token,用户携带Token去访问微服务

每个微服务都可以解密Token,

需要防止密钥泄露

优点:实现不复杂,降低了网关的复杂度

缺点:密钥泄露是软肋

各种方法的对比
JWT%204ce37e5d266b4867b3bd93740fcb84e9/Untitled%207.png

访问控制模型

Role-based access control (RBAC模型)

我们选择最常用的RBAC角色权限控制模型作为DEMO展示

JWT%204ce37e5d266b4867b3bd93740fcb84e9/Untitled%208.png

JWT

Json web token

  1. 用户使用浏览器发送账号和密码
  2. 服务器使用私钥创建一个JWT
  3. 服务器返回该JWT给浏览器
  4. 浏览器将该JWT串在请求头中向服务器发送请求
  5. 服务器验证该JWT
  6. 根据授权规则返回资源给浏览器

JWT组成

不能把敏感信息放在Payload中

JWT%204ce37e5d266b4867b3bd93740fcb84e9/Untitled%209.png

公式

JWT%204ce37e5d266b4867b3bd93740fcb84e9/Untitled%2010.png

导入JWT

http://www.imooc.com/article/290892

加依赖

<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-api</artifactId>
  <version>0.10.7</version>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-impl</artifactId>
  <version>0.10.7</version>
  <scope>runtime</scope>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-jackson</artifactId>
  <version>0.10.7</version>
  <scope>runtime</scope>
</dependency>

JWT工具类,可以生成Token,以及判断Token是否合法

package com.example.gateway.utils;

import com.alibaba.nacos.client.identify.Base64;
import com.google.common.collect.Maps;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@RequiredArgsConstructor
@SuppressWarnings("WeakerAccess")
@Component
public class JwtOperator {
    /**
     * 秘钥
     * - 默认aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt
     */
    @Value("${jwt.secret:aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt}")
    private String secret;
    /**
     * 有效期,单位秒
     * - 默认2周
     */
    @Value("${jwt.expire-time-in-second:1209600}")
    private Long expirationTimeInSecond;

    /**
     * 从token中获取claim
     *
     * @param token token
     * @return claim
     */

    public Claims getClaimsFromToken(String token) {
        try {
            return Jwts.parser()
                .setSigningKey(this.secret.getBytes())
                .parseClaimsJws(token)
                .getBody();
        } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | IllegalArgumentException e) {
            log.error("token解析错误", e);
            throw new IllegalArgumentException("Token invalided.");
        }
    }

    /**
     * 获取token的过期时间
     *
     * @param token token
     * @return 过期时间
     */
    public Date getExpirationDateFromToken(String token) {
        return getClaimsFromToken(token)
            .getExpiration();
    }

    /**
     * 判断token是否过期
     *
     * @param token token
     * @return 已过期返回true,未过期返回false
     */
    private Boolean isTokenExpired(String token) {
        Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    /**
     * 计算token的过期时间
     *
     * @return 过期时间
     */
    private Date getExpirationTime() {
        return new Date(System.currentTimeMillis() + this.expirationTimeInSecond * 1000);
    }

    /**
     * 为指定用户生成token
     *
     * @param claims 用户信息
     * @return token
     */
    public String generateToken(Map<String, Object> claims) {
        Date createdTime = new Date();
        Date expirationTime = this.getExpirationTime();

        byte[] keyBytes = secret.getBytes();
        SecretKey key = Keys.hmacShaKeyFor(keyBytes);

        return Jwts.builder()
            .setClaims(claims)
            .setIssuedAt(createdTime)
            .setExpiration(expirationTime)
            // 你也可以改用你喜欢的算法
            // 支持的算法详见:https://github.com/jwtk/jjwt#features
            .signWith(key, SignatureAlgorithm.HS256)
            .compact();
    }

    /**
     * 判断token是否非法
     *
     * @param token token
     * @return 未过期返回true,否则返回false
     */
    public Boolean validateToken(String token) {
        return !isTokenExpired(token);
    }

		// 测试方法
    public static void main(String[] args) {
        // 1. 初始化
        JwtOperator jwtOperator = new JwtOperator();
        jwtOperator.expirationTimeInSecond = 1209600L;
        jwtOperator.secret = "aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttr";

        // 2.设置用户信息
        HashMap<String, Object> objectObjectHashMap = Maps.newHashMap();
        objectObjectHashMap.put("id", "1");

        // 测试1: 生成token
        String token = jwtOperator.generateToken(objectObjectHashMap);
        // 会生成类似该字符串的内容: eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJpYXQiOjE1NjU1ODk4MTcsImV4cCI6MTU2Njc5OTQxN30.27_QgdtTg4SUgxidW6ALHFsZPgMtjCQ4ZYTRmZroKCQ
        System.out.println(token);

        // 将我改成上面生成的token!!!
        String someToken = "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJpYXQiOjE1OTQ5MDMwOTEsImV4cCI6MTIyNTU1MDAzMDkwfQ.a3vyNQz4nbgOo5wB-_69wscmfvQfBgkvm_Q8H_IkTqc";
        // 测试2: 如果能token合法且未过期,返回true
        Boolean validateToken = jwtOperator.validateToken(someToken);
        System.out.println(validateToken);
//
        // 测试3: 获取用户信息
        Claims claims = jwtOperator.getClaimsFromToken(someToken);
        System.out.println(claims);
//
        // 将我改成你生成的token的第一段(以.为边界)
        String encodedHeader = "eyJhbGciOiJIUzI1NiJ9";
        // 测试4: 解密Header
        byte[] header = Base64.decodeBase64(encodedHeader.getBytes());
        System.out.println(new String(header));

        // 将我改成你生成的token的第二段(以.为边界)
        String encodedPayload = "eyJpZCI6IjEiLCJpYXQiOjE1NjU1ODk1NDEsImV4cCI6MTU2Njc5OTE0MX0";
        // 测试5: 解密Payload
        byte[] payload = Base64.decodeBase64(encodedPayload.getBytes());
        System.out.println(new String(payload));

        // 测试6: 这是一个被篡改的token,因此会报异常,说明JWT是安全的
        jwtOperator.validateToken("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJpYXQiOjE1NjU1ODk3MzIsImV4cCI6MTU2Njc5OTMzMn0.nDv25ex7XuTlmXgNzGX46LqMZItVFyNHQpmL9UQf-aUx");
    }
}

JWT分为三段,头部,以及payload,还有签名,可以使用工具类测试

可以将该密钥写入配置类,这里涉及一个配置技巧

jwt:
  secret: 秘钥
  # 有效期,单位秒,默认2周
  expire-time-in-second: 1209600

微信登录

登录方法

JWT%204ce37e5d266b4867b3bd93740fcb84e9/Untitled%2011.png

需要我们写一个 /users/login 的接口

新建一个类 JwtTokenRespDTO,存储展示的Token信息

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class JwtTokenRespDTO {
    private String token;
    private Long expirationTime;
}

UserRespDTO,存储展示的用户信息

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class UserRespDTO {
    /**
     * id
     */
    private Integer id;
    /**
     * 头像地址
     */
    private String avatarUrl;
    /**
     * 积分
     */
    private String bonus;
    /**
     * 微信昵称
     */
    private String wxNickname;
}

LoginRespDTO,用来返回登录信息(用户信息以及Token信息)

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class LoginRespDTO {
    private JwtTokenRespDTO jwtTokenRespDTO;
    private UserRespDTO user;
}

UserLoginDTO,用来微信登录输入消息

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class UserLoginDTO {
    private String code;
    private String avatarUrl;
    private String wxNickname;
}

加入一个依赖

<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-miniapp</artifactId>
    <version>3.5.0</version>
</dependency>

业务逻辑

  1. 调用Wx登录接口,查询用户是否在数据库中存在,
  2. 若存在则给予一个Token,不存在插入到数据库中,再给予Token

wx登录接口,输入app id以及密钥

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WxConfig {
    @Bean
    public WxMaConfig wxMaConfig(){
        WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
				// 这边写微信的appId以及
        config.setAppid("xxwxf951e829c4a34a45x");
        config.setSecret("xxd8bb0f730ffa14782f08e59107c1fc88xx");
        return config;
    }

    @Bean
    public WxMaService wxMaService(WxMaConfig wxMaConfig){
        WxMaServiceImpl service = new WxMaServiceImpl();
        service.setWxMaConfig(wxMaConfig);
        return service;
    }
}

服务类

public User login(UserLoginDTO loginDTO,String openId){

  User user = userMapper.selectOne(new QueryWrapper<User>()
																					.eq("wx_id", openId));

  if (user == null){
      User userToSave = User.builder()
              .wxId(openId)
              .bonus(300)
              .wxNickname(loginDTO.getWxNickname())
              .avaterUrl(loginDTO.getAvatarUrl())
              .roles("user")
              .createTime(new Date())
              .updateTime(new Date())
              .build();
      userMapper.insert(userToSave);
      return userToSave;
  }
  return user;
}

写登录api,具体逻辑在注释里已经标有

@PostMapping("/login")
public LoginRespDTO login(@RequestBody UserLoginDTO loginDTO) throws WxErrorException {
    // 校验是否登录
    WxMaJscode2SessionResult result = wxMaService.getUserService()
            .getSessionInfo(loginDTO.getCode());
    System.out.println("code:"+loginDTO.getCode());

    // 微信的唯一标示
    String openid = result.getOpenid();

    // 看用户是否在用户中心注册,如果没有就插入数据库
    // 之后颁发token
    User user = userService.login(loginDTO, openid);

    // 颁发jwt,token
    HashMap<String, Object> userInfo = Maps.newHashMap();
    userInfo.put("id",user.getId());
    userInfo.put("wxNickname",user.getWxNickname());
    userInfo.put("role",user.getRoles());
    String token = jwtOperator.generateToken(userInfo);
		// 输出日志
    log.info("用户{}登陆成功,生成的token={},有效期到:{}",
            loginDTO.getWxNickname(),
            token,
            jwtOperator.getExpirationTime());
		
    UserRespDTO userRespDTO = UserRespDTO.builder()
            .id(user.getId())
            .avatarUrl(user.getAvaterUrl())
            .bonus(user.getBonus())
            .wxNickname(user.getWxNickname())
            .build();

    JwtTokenRespDTO jwtTokenRespDTO = JwtTokenRespDTO.builder()
            .expirationTime(jwtOperator.getExpirationTime().getTime())
            .token(token)
            .build();

    LoginRespDTO loginRespDTO = LoginRespDTO.builder()
            .user(userRespDTO)
            .jwtTokenRespDTO(jwtTokenRespDTO)
            .build();

    return loginRespDTO;

}

AOP实现登录状态检查

需要预备知识

  1. 注解知识

  2. AOP知识

  3. 定义一个注解

思路

因为 /users/id 只能在登录后访问,因此需要状态检查

  1. 写过滤器,获得Token,是否合法或者过期
  2. 拦截器
  3. Spring AOP

第三种方式可插拔,AOP可以实现登录检查

  1. 导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 写一个注解 @CheckLogin,默认形式即可
public @interface CheckLogin {
}
  1. AOP类

这里使用了Around,其实也可以使用before

  1. 万能Request获取法
  2. 万能错误返回处理法
@Aspect
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CheckLoginAspect {

    private final JwtOperator jwtOperator;

    @Pointcut("@annotation(com.zhou.usercenter.auth.CheckLogin)")
    public void checkLoginPointCut(){}

    @Around("checkLoginPointCut()")
    public Object checkLogin(ProceedingJoinPoint jp) throws Throwable {
        try {
            // 1.从header中获得Token
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = attributes.getRequest();
            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"));

            return jp.proceed();
        } catch (Throwable throwable) {
            throw new SecurityException("Token不合法");
        }

    }
}

3.1 (万能获得Request的方法)

RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest request = attributes.getRequest();
  1. 错误返回

@RestControllerAdvice 异常统一拦截返回

ResponseEntity的用法:返回体+返回响应码

@RestControllerAdvice
@Slf4j
public class GlobalExceptionErrorHandler {
    @ExceptionHandler(SecurityException.class)
    public ResponseEntity<ErrorBody> error(SecurityException e){
        log.warn("发生SecurityException异常",e);
        ErrorBody errorBody = ErrorBody.builder()
                .body("Token非法,用户不允许访问")
                .status(HttpStatus.UNAUTHORIZED.value())
                .build();
        ResponseEntity<ErrorBody> response = new ResponseEntity<ErrorBody>(
                errorBody, HttpStatus.UNAUTHORIZED
        );
        return response;
    }
}

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
class ErrorBody{
    private String body;
    private int status;

}
  1. Controller
@RestController
@RequestMapping("/users")
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserController {

    private final UserServiceImpl userService;
    private final WxMaService wxMaService;
    private final JwtOperator jwtOperator;

    @GetMapping("/{id}")
    @CheckLogin
    public User findById(@PathVariable("id") Integer id){
        log.info("我被请求勒.....");
        return userService.findById(id);
    }

    @GetMapping("/gen-token")
    public String genToken(){
        // 颁发jwt,token
        HashMap<String, Object> userInfo = Maps.newHashMap();
        userInfo.put("id",1);
        userInfo.put("wxNickname","周杰伦");
        userInfo.put("role","user");
        return jwtOperator.generateToken(userInfo);
    }

    @PostMapping("/login")
    public LoginRespDTO login(@RequestBody UserLoginDTO loginDTO) throws WxErrorException {
        // 校验是否登录
        WxMaJscode2SessionResult result = wxMaService.getUserService()
                .getSessionInfo(loginDTO.getCode());
        System.out.println("code:"+loginDTO.getCode());
        // 微信的唯一标示
        String openid = result.getOpenid();

        // 看用户是否在用户中心注册,如果没有就插入数据库
        // 之后颁发token
        User user = userService.login(loginDTO, openid);

        // 颁发jwt,token
        HashMap<String, Object> userInfo = Maps.newHashMap();
        userInfo.put("id",user.getId());
        userInfo.put("wxNickname",user.getWxNickname());
        userInfo.put("role",user.getRoles());
        String token = jwtOperator.generateToken(userInfo);
        log.info("用户{}登陆成功,生成的token={},有效期到:{}",
                loginDTO.getWxNickname(),
                token,
                jwtOperator.getExpirationTime());

        UserRespDTO userRespDTO = UserRespDTO.builder()
                .id(user.getId())
                .avatarUrl(user.getAvaterUrl())
                .bonus(user.getBonus())
                .wxNickname(user.getWxNickname())
                .build();

        JwtTokenRespDTO jwtTokenRespDTO = JwtTokenRespDTO.builder()
                .expirationTime(jwtOperator.getExpirationTime().getTime())
                .token(token)
                .build();

        LoginRespDTO loginRespDTO = LoginRespDTO.builder()
                .user(userRespDTO)
                .jwtTokenRespDTO(jwtTokenRespDTO)
                .build();

        return loginRespDTO;

    }

}

之后访问gen-token获得登录token,再将其带到header中访问/users/{id},即可实现登录授权

内容中心登录状态检查

Feign的Token传递

  1. 代码移植
  2. 在请求响应中发现,请求用户中心的请求没有被授权

使用Feign调用用户中心的时候没有传递Token

因此需要用Feign传递Token

@RequestHeader方法

shares/{id} api

@GetMapping("/{id}")
@CheckLogin
public ShareDTO findById(
        @PathVariable("id") Integer id,
        @RequestHeader("X-Token") String token){
    ShareDTO shareDTO = shareService.findById(id,token);
    return shareDTO;
}

service类

public ShareDTO findById(Integer id,String token)

Feign类

@FeignClient(name="user-center"
//        , fallbackFactory = UserCenterFeignClientFallbackFactory.class
)
public interface UserCenterFeignClient {
    @GetMapping("/users/{id}")
    UserDTO findById(@PathVariable("id") Integer id,
                     @RequestHeader("X-Token") String token);

}

RequestInterceptor传递法

全局拦截器,使用requestTemplate传递Header

public class TokenRelayRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        // 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)){
            requestTemplate.header("X-Token",token);
        }

    }
}

之后在application.yml中配置全局配置

feign:
  client:
    config:
      default:
        loggerLevel: full
        requestInterceptors:
          - com.zhou.contentcenter.feignclient.interceptor.TokenRelayRequestInterceptor

RestTemplate传递Token方法

exchange方法

@Autowired
private RestTemplate restTemplate;

@GetMapping("/tokenRelay/{userId}")
public ResponseEntity<UserDTO> userDTO(@PathVariable("userId") Integer userId,
                                       HttpServletRequest request){
    String token = request.getHeader("X-Token");
    HttpHeaders headers = new HttpHeaders();
    headers.add("X-Token",token);

    ResponseEntity<UserDTO> exchange = restTemplate.exchange(
            "http://user-center/users/{userId}",
            HttpMethod.GET,
            new HttpEntity<>(headers), // 
            UserDTO.class, // 响应体类型
            userId // URL参数
    );
    return exchange;
}

ClientHttpRequestInterceptor

public class RestTemplateTokenRelayInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest httpServletRequest = attributes.getRequest();

        String token = httpServletRequest.getHeader("X-Token");

        HttpHeaders headers = request.getHeaders();
        headers.add("X-Token",token);

        // 保证请求继续执行
        return execution.execute(request,body);
    }
}

configuration类

@Configuration
public class ContentRestTemplateConfig {
    @Bean
    @LoadBalanced
    @SentinelRestTemplate
    public RestTemplate restTemplate(){

        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setInterceptors(
                Collections.singletonList(
                        new RestTemplateTokenRelayInterceptor()
                )
        );
        return restTemplate;
    }
}

controller类

@GetMapping("/tokenRelay2/{userId}")
public UserDTO userDTO2(@PathVariable("userId") Integer userId){
    UserDTO userDTO = restTemplate.getForObject(
            "http://user-center/users/{userId}",
            UserDTO.class,userId);
    return userDTO;
}

角色权限验证判断

加入Request判断

但是每次更改都很麻烦,如果有很多接口则需要更改好几个接口,有些麻烦

@PutMapping("/audit/{id}")
@CheckLogin
public Share auditById(@PathVariable("id") Integer id,
                       @RequestBody ShareAuditDTO auditDTO,
                       HttpServletRequest httpServletRequest){
    String role = (String) httpServletRequest.getAttribute("role");
    log.info("当前角色是:{}",role);
    // TODO 认证授权
    return shareService.auditById(id,auditDTO);
}

Spring AOP注解方式实现权限验证

写一个注解,记得加入Retention

@Retention(RetentionPolicy.RUNTIME)
public @interface CheckAuthorization {
    String value();
}

切面

获取注解中的名字值得学习

@Aspect
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CheckAuthorizationAspect {
    @Autowired
    private JwtOperator jwtOperator;

    @Pointcut("@annotation(com.zhou.contentcenter.auth.CheckAuthorization)")
    public void CheckAuthorizationPointCut(){ }

    @Around("CheckAuthorizationPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        try {
            // 1.从header中获得Token
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = attributes.getRequest();
            String token = request.getHeader("X-Token");

            // 2. 校验token是否合法或是否过期,
            Boolean isValid = jwtOperator.validateToken(token);
            if (!isValid) {
                throw new SecurityException("Token不合法");
            }

            // 3.校验成功,将用户信息设置到request的attribute里
            // 从生成Token的Claims里边获取值,输入到Request中
            Claims claims = jwtOperator.getClaimsFromToken(token);
            request.setAttribute("id", claims.get("id"));
            request.setAttribute("wxNickname", claims.get("wxNickname"));
            request.setAttribute("role", claims.get("role"));

            String role = (String) claims.get("role");
            System.out.println("用户的角色是:"+role);
            // 获取到Signature的名称
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();
            CheckAuthorization annotation = method.getAnnotation(CheckAuthorization.class);
            String value = annotation.value();
            System.out.println("用户的角色是:"+value);

            if (!Objects.equals(value, role)) {
                throw new SecurityException("用户无权访问");
            }

        }catch (Throwable throwable){
            throw new SecurityException("用户无权访问",throwable);
        }
        return point.proceed();
    }

}

定义好上述之后,只需在需要控制权限的接口controller函数上加上注解@CheckAuthorization("admin")即可实现对登录用户的权限访问

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值