lotus

贵有恒何必三更眠五更起 最无益只怕一日曝十日寒

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

如何使用token进行身份认证

什么是token?

token是一种用于身份认证的技术,它是一个包含用户信息和签名的字符串,通常由服务器生成并返回给客户端。客户端在每次请求时,都需要携带token来证明自己的身份。服务器通过验证token的有效性和完整性,来授权客户端访问受保护的资源。

为什么要使用token?

使用token进行身份认证有以下几个优点:

  • 无状态:token不需要服务器存储或维护,可以减轻服务器的负担,提高性能和可扩展性。
  • 安全:token可以防止重放攻击,因为每个token都有一个过期时间,过期后就无法使用。token也可以防止篡改攻击,因为每个token都有一个签名,如果被修改,签名就会失效。
  • 灵活:token可以支持多种认证方式,例如用户名密码、第三方登录、短信验证码等。token也可以支持多种授权方式,例如基于角色、基于权限、基于范围等。

如何设计token?

一个典型的token由三部分组成:头部(header)、载荷(payload)和签名(signature)。它们之间用点号(.)分隔,例如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoxNjE0NzUwNzQ3LCJyb2xlIjoiYWRtaW4ifQ.7y8nZbLh8q1x6rZy7kKlHwv5fXs2fLqk8vVQfYm4c0o
  • 头部(header):包含了token的类型(typ)和使用的算法(alg),通常使用JSON格式,并进行Base64编码。
  • 载荷(payload):包含了用户的信息(sub, name)和其他的声明(exp, role),通常使用JSON格式,并进行Base64编码。
  • 签名(signature):由头部和载荷通过算法(alg)和密钥(secret)生成的散列值,用于验证token的完整性。

如何实现token?

在JAVA语言中,可以使用一些开源的库来实现token的生成和验证,例如jjwt, nimbus-jose-jwt等。以下是一个基于token身份认证的完整实例,它使用了Java语言和Redis数据库。它的主要步骤如下:

  • 在web.xml中配置需要认证的请求,例如/**/userApi/**/**
  • 实现一个过滤器类(AuthFilter),用于拦截需要认证的请求,并调用一个认证类(Author)来验证token。
  • 实现一个认证类(Author),用于从请求头或参数中获取token,并从Redis中获取对应的用户ID。如果用户ID存在,说明token有效,将用户ID存入Redis并放行请求;如果用户ID不存在,说明token无效或过期,拒绝请求并返回错误信息。
  • 实现一个登录接口(LoginController),用于接收用户的用户名和密码,并验证用户的合法性。如果用户合法,生成一个token,并将token和用户ID存入Redis,并返回token给客户端;如果用户不合法,返回错误信息给客户端。
  • 实现一个获取用户信息的接口(UserController),用于根据token从Redis中获取用户ID,并根据用户ID从数据库中获取用户信息,并返回给客户端。

 

以下是JAVA实例的代码:

// 导入相关的类
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

import java.security.Key;
import java.util.Date;

// 生成一个密钥
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);

// 过滤器类
public class AuthFilter implements Filter {
    protected Pattern[] ignorePattern = null;
    PathMatcher matcher = new AntPathMatcher();
    // 不用过滤的请求
    String[] ignoreStrs = null;
    // 需要过滤的请求
    String[] noIgnoreStrs = null;

    public AuthFilter() {
    }

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        String uri = request.getRequestURI();
        // 判断是否需要认证
        if (this.checkIgnore(uri) && !this.checkNoIgnore(uri)) {
            chain.doFilter(request, response);
        } else {
            // 用户认证
            Author.checkAuthorLocal(request, response, chain);
        }
    }

    @Override
    public void init(FilterConfig config) throws ServletException {
        String ignore = config.getInitParameter("ignore");
        String noIgnore = config.getInitParameter("noIgnore");
        ignore = StringUtils.defaultIfEmpty(ignore, "");
        this.ignoreStrs = ignore.replaceAll("\\s", "").split(",");
        this.noIgnoreStrs = noIgnore.replaceAll("\\s", "").split(",");
    }

    public boolean checkIgnore(String requestUrl) {
        boolean flag = false;
        String[] var6 = this.ignoreStrs;
        int var5 = this.ignoreStrs.length;
        for(int var4 = 0; var4 < var5; ++var4) {
            String pattern = var6[var4];
            if (flag = this.matcher.match(pattern, requestUrl)) {
                break;
            }
        }
        return flag;
    }

    public boolean checkNoIgnore(String requestUrl) {
        boolean flag = false;
        String[] var6 = this.noIgnoreStrs;
        int var5 = this.noIgnoreStrs.length;
        for(int var4 = 0; var4 < var5; ++var4) {
            String pattern = var6[var4];
            if (flag = this.matcher.match(pattern, requestUrl)) {
                break;
            }
        }
        return flag;
    }
}

// 认证类
public class Author {
    public Author() {
    }

    public static void checkAuthorLocal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (!request.getMethod().equals("OPTIONS")) {
            // 获取token
            String token = request.getHeader("oauth_token");
            if (!StringUtil.hasText(token)) {
                token = request.getParameter("oauth_token");
            }
            String loginUserId;
            if (StringUtil.hasText(token)) {
                // 判断token有效性
                loginUserId = getLoginUserId(token);
                if (StringUtil.hasText(loginUserId)) {
                    setLoginUserId(token, loginUserId);
                    chain.doFilter(request, response);
                } else {
                    response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                    PrintWriter writer = response.getWriter();
                    writer.print("no access");
                    return;
                }
            } else {
                response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                PrintWriter writer = response.getWriter();
                writer.print("no access");
                return;
            }
        }
    }

    private static String getLoginUserId(String accessToken) {
        String userId = RedisClient.get(accessToken);
        return userId;
    }

    public static void setLoginUserId(String accessToken, String userId) {
        try {
            RedisClient.set(accessToken, userId, Cluster.getTimeoutSecond());
        } catch (Exception var3) {
            var3.printStackTrace();
        }
    }
}

// 登录接口
@RestController
@RequestMapping("/login")
public class LoginController {

    @Autowired
    private UserService userService;

    @PostMapping("/login")
    public Result login(@RequestBody User user) {
        // 验证用户合法性
        User dbUser = userService.findByUsernameAndPassword(user.getUsername(), user.getPassword());
        if (dbUser != null) {
            // 生成token
            String token = Jwts.builder()
                    .setSubject(dbUser.getId()) // 设置用户ID
                    .setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 设置过期时间为1小时
                    .signWith(key) // 使用密钥签名
                    .compact(); // 生成字符串

            // 将token和用户ID存入Redis
            Author.setLoginUserId(token, dbUser.getId());

            // 返回token给客户端
            return Result.success(token);
        } else {
            // 返回错误信息给客户端
            return Result.fail("用户名或密码错误");
        }
    }

}

// 获取用户信息的接口
@RestController
@RequestMapping("/userApi")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/getUserInfo")
    public Result getUserInfo(HttpServletRequest request) {
        // 根据token从Redis中获取用户ID
        String token = request.getHeader("oauth_token");
        String userId = Author.getLoginUserId(token);

        // 根据用户ID从数据库中获取用户信息
        User user = userService.findById(userId);

        // 返回用户信息给客户端
        return Result.success(user);
    }

}

 

总结

使用token进行身份认证是一种流行的技术,它有无状态、安全和灵活的特点。token由头部、载荷和签名组成,可以使用一些开源的库来实现token的生成和验证。本文介绍了一个基于token身份认证的完整实例,它使用了Java语言和Redis数据库。希望这篇博客对你有所帮助。

posted on 2023-07-03 17:11  白露~  阅读(790)  评论(0编辑  收藏  举报