使用JWT和Spring Security结合开发一套认证鉴权系统

image.png

为使用前后端分离的后端管理框架做准备,使用JWT目前看来是最佳选择。

知其然

  • 无状态
  • 服务化
  • 移动、web统统支持,后端提供相关接口即可

知其所以然

  1. 认证:通过调用登录接口,服务器颁发一个JWT(JSON Web Token)给客户端。
  2. 鉴权:客户端每个请求携带JWT,后端使用一个请求过滤器,从请求头中解析JWT,为本次请求授予相关权限

导入io.jsonwebtoken:jjwt

jjwt提供了JWT主要功能的Java版本实现,目前版本是0.9.0

dependencies {
    compile 'io.jsonwebtoken:jjwt:0.9.0'
}

生成token

private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims) //自定义声明
                .setExpiration(generateExpirationDate()) //过期时间
                .signWith(SignatureAlgorithm.HS512, secret) //加密
                .compact();
    }

解析token

private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

鉴权过滤器

package cn.wycode.web.config.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Value("${jwt.header}")
    private String tokenHeader;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain chain) throws ServletException, IOException {

        String authHeader = request.getHeader(this.tokenHeader);
        if (authHeader != null && authHeader.startsWith(tokenHead)) {
            final String authToken = authHeader.substring(tokenHead.length()); // The part after "Bearer "
            String account = jwtTokenUtil.getUsernameFromToken(authToken);

            logger.info("checking authentication " + account);

            if (account != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(account);

                if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                            userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(
                            request));
                    logger.info("authenticated user " + account + ", setting security context");
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }

        chain.doFilter(request, response);
    }
}

配置Spring security

    httpSecurity.addFilterBefore(authenticationTokenFilterBean(),UsernamePasswordAuthenticationFilter.class);

本文原始发表于: https://wycode.cn/blog/jwt, 转载请注明出处

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容