基于JWT的API权限鉴定

0.前言

   JWT的全称叫Json Web Token;在前端和后台进行数据交互的时候,在请求头中带上一个token 字符串,这个字符串中保存了用户具有的权限信息。同时也是经过加密的,不容易被破解。
  在项目中,我们使用token来进行权限的鉴定。用户登录信息匹配的时候,我们就获取当前用户具有的api权限标识符。把这些标识符放在放在token中,并返回给客户端(token是进行过加密的,不用担心安全问题)。客户端发送第二请求的时候,就会吧这个token放在请求头中。服务器端的拦截器或者这个请求头信息,并解析出token中的api权限标识符,如果当前请求的标识可以在token中匹配到,那么就通过请求。这就是token鉴权的大致过程。

1.Maven引入JJWT

<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2. 编写测试代码

public class CreateJwtTest {

    /**
     * 使用jjwt创建token
     * @param
     */
    @Test
    public void test01() {
        JwtBuilder jwtBuilder = Jwts.builder().setId("123")
                .setSubject("alu4r")//用户相关信息
                .setIssuedAt(new Date())//设置创建的时间
                .signWith(SignatureAlgorithm.HS256, "alu4r")//为token加密
                .claim("key","value");
        Map<String, Object> map = new HashMap<>();
        map.put("11", "11");
        map.put("22", "22");
        jwtBuilder.setClaims(map);
        String token = jwtBuilder.compact();
        System.out.println(token);
    }

    /**
     * 解析jwt token字符串
     */
    @Test
    public void Test02(){
        String token = "eyJhbGciOiJIUzI1NiJ9.eyIxMSI6IjExIiwiMjIiOiIyMiJ9.JIzRbr5R2D9cfNFgOscGM_PD_mXdW_zJGMWmmc8Ddgs";
        Claims claims = Jwts.parser().setSigningKey("阿alu4r").parseClaimsJws(token).getBody();
        //数据都放在了claims中
        System.out.println(claims.getId()+"==>"+claims.getSubject()+"==>"+claims.getIssuedAt());
        //获取自定义的claims
        System.out.println(claims.get("11"));
    }
}

3.jwt生成和解析token工具类

@Getter
@Setter
@EnableConfigurationProperties(JwtUtils.class)
@ConfigurationProperties(prefix = "jwt.config")
public class JwtUtils {
    /**
     * 签名私钥
     */
    private String key;
    /**
     * 签名失效时间
     */
    private Long ttl;

    /**
     * 解析token,获取claims
     */
    public Claims parseJwt(String token) {
        Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
        return claims;
    }
    /**
     * 设置认证token
     * id : 用户的id
     * name : 用户名
     * map : 用户的私有数据
     */
    public String createJwt(String id, String subject, Map<String, Object> map){
        //设置失效时间
        //当前毫秒数
        long now = System.currentTimeMillis();
        //失效后的毫秒数
        long exp = now + ttl;
        //用户相关信息、设置创建的时间、为token加密
        JwtBuilder jwtBuilder = Jwts.builder().setId(id)
                .setSubject(subject)
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256, key);
        //添加私有map数据
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            jwtBuilder.claim(entry.getKey(), entry.getValue());
        }
        //设置失效的时间
        jwtBuilder.setExpiration(new Date(exp));
        String token = jwtBuilder.compact();
        return token;
    }
}

  工具类中使用了@ConfigurationProperties(prefix = “jwt.config”)注解,key和有效时间可以在配置文件(yml)中进行配置。

4. 在token中设置api权限

  用户在登录成功的时候,我们可以在数据库中查询当前用户有哪些api权限,把这些api权限的标识放在token中;
  登录控制器:

/**
*Result : 返回到客户端的数据
*Role : 角色
*Permission : 权限
*/
@PostMapping("/login")
    public Result login(@RequestBody Map<String, String> loginMap){
        String mobile = loginMap.get("mobile");
        String password = loginMap.get("password");
        User user = userService.findByMobile(mobile);
        Result result = null;
        if(user == null || !Objects.equals(password,user.getPassword())){
            result = new Result(ResultCode.MOBILE_OR_PASSWORD);
        }else {
            //获取所有的可访问api权限
            StringBuilder sb = new StringBuilder();
            for (Role role : user.getRoles()) {
                for (Permission permission : role.getPermissions()) {
                    if(permission.getType() == PermissionConstants.PERMISSION_API){
                        sb.append(permission.getCode()).append(",");
                    }
                }
            }
            Map<String, Object> map = new HashMap<>();
            map.put("apis", sb.toString());
            map.put("companyId",user.getCompanyId());
            map.put("companyName",user.getCompanyName());
            //将apis的权限标识符,和其他的一些需要的数据放在token中
            String token = jwtUtils.createJwt(user.getId(), user.getUsername(), map);
            result = new Result(ResultCode.SUCCESS,token);
        }
        return result;
    }

5. 解析获取token中的数据

  在项目中我们把解析token的代码放在拦截器中,这样我们就可以对每一个请进行权限验证了

@Component
public class JwtInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    JwtUtils jwtUtils;
    /**
     * 进入控制器之前执行的方法
     *      简化获取token数据的代码的编写
     *      同一的用户权限校验
     *
     *  判断用户是否有当前的接口权限
     * @param request
     * @param response
     * @param handler
     * @return  是否继续执行
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取到token
        String authorization = request.getHeader("Authorization");
        //判断请求头信息是否为空, 或者是否以Bearer开头
        if(!StringUtils.isEmpty(authorization) && authorization.startsWith("Bearer ")){
            String token = authorization.replace("Bearer ","");
            Claims claims = jwtUtils.parseJwt(token);
            if(claims != null){
                String apis = (String) claims.get("apis");
                //通过handler,获取控制器api接口名,也就是PostMapping注解中name中的属性
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                RequestMapping methodAnnotation = handlerMethod.getMethodAnnotation(RequestMapping.class);
                String name = methodAnnotation.name();
                //如果匹配到权限标识符 就通过请求
                if(apis.contains(name)){
                	//用来保存私有数据的,
                    request.setAttribute("user_claims",claims);
                    return true;
                }else {
                    throw new CommonException(ResultCode.UNAUTHORISE);
                }
            }
        }else {
            throw new CommonException(ResultCode.UNAUTHENTICATED);
        }
        return true;
    }
}

代码是截取项目中的部分,不能保证copy就能运行,不对之处,请多指教!

你可能感兴趣的:(组件)