黑马程序员技术交流社区

标题: 【杭州校区】一个基于springSecurity的Json Web Token的实现 [打印本页]

作者: 小江哥    时间: 2019-10-23 17:32
标题: 【杭州校区】一个基于springSecurity的Json Web Token的实现
SecurityJwt一个基于springSecurity的Json Web Token的实现
GitHub地址

提要一、SpringSecurity二、JSON Web Token三、开发环境介绍
build.gradle文件:
plugins {    id 'org.springframework.boot' version '2.2.0.RELEASE'    id 'io.spring.dependency-management' version '1.0.8.RELEASE'    id 'java'}group = 'org.zuoyu'version = '1.0.0'sourceCompatibility = '1.8'configurations {    compileOnly {        extendsFrom annotationProcessor    }}repositories {//    这里使用的是阿里巴巴的Maven仓库    maven {        url 'http://maven.aliyun.com/nexus/content/groups/public/'    }}dependencies {    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'    implementation 'org.springframework.boot:spring-boot-starter-security'    implementation 'org.springframework.boot:spring-boot-starter-web'    compileOnly 'org.projectlombok:lombok'    runtimeOnly 'mysql:mysql-connector-java'    annotationProcessor 'org.projectlombok:lombok'//    jwt依赖    runtime('io.jsonwebtoken:jjwt-jackson:0.10.7')    runtime('io.jsonwebtoken:jjwt-impl:0.10.7')    compile('io.jsonwebtoken:jjwt-api:0.10.7')    testImplementation('org.springframework.boot:spring-boot-starter-test') {        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'    }    testImplementation 'org.springframework.security:spring-security-test'}test {    useJUnitPlatform()}四、源码说明在这里只分析关键代码(其中的JwtConstants.java是我自定义的final变量类)备注: 在security的配置文件中,将session管理器关闭,没有必要使用session。1. JwtTokenUtils.java(JWT的工具类)/**   * 构建JWT   *   * @param subject - 实体   * @param authorities - 权限   * @param expiration - 保留时间   * @return - token   */  private static String createJwt(String subject,      String authorities, long expiration) {    long nowMillis = System.currentTimeMillis();    return Jwts.builder()        .setId(JwtConstants.createTokenId())        .signWith(SECRET_KEY, SignatureAlgorithm.HS256)        .setIssuer(JwtConstants.JWT_ISSUER)        .setSubject(subject)        .claim(JwtConstants.ROLE_CLAIMS, authorities)        .setIssuedAt(new Date(nowMillis))        .setNotBefore(new Date(nowMillis))        .setExpiration(new Date(nowMillis + expiration * 1000L))        .compact();  }
在这里我们使用官方依赖包中的Jwts.builder()方法,创建一个token,其中——
解析token:
/**   * 解析token   *   * @param token -   * @return - Claims   */  private static Claims parseJwt(String token) {    return Jwts.parser()        .setSigningKey(SECRET_KEY)        .parseClaimsJws(token)        .getBody();  }
在这里重点在与setSigningKey,传入我们在创建时候的私密钥SECRET_KEY。
还有几个与security方便交互的方法:
/**   * 根据账户构建token   *   * @param user - 账户   * @return -   */  public static String createToken(User user, boolean isRememberMe) {    long expiration =        isRememberMe ? JwtConstants.EXPIRATION_REMEMBER : JwtConstants.EXPIRATION;    String spacer = ",";    List<string> authorities = Arrays.stream(user.getRoles().split(spacer))        .map(role -&gt; "ROLE_" + role)        .collect(Collectors.toList());    return createJwt(JsonUtil.beanToJsonString(user), JsonUtil.objectToJsonString(authorities),        expiration);  }  /**   * 获取用户   *   * @param token - token   * @return - User   */  public static User getUserByToken(String token) {    String subject = parseJwt(token).getSubject();    return JsonUtil.jsonStringToBean(subject, User.class);  }/**   * 获取用户的权限   * @param token - token   * @return - 权限列表   */  public static Collection<!--? extends GrantedAuthority--> getAuthoritiesByToken(String token) {    String roles = parseJwt(token).get(JwtConstants.ROLE_CLAIMS).toString();    return JsonUtil.jsonStringToCollection(roles, SimpleGrantedAuthority.class);  }2. AuthenticationSuccessHandlerImpl.java(Security登录成功后的执行行为)/** * 登录成功的实现. * * @author zuoyu **/@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {  @Override  public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,      Authentication authentication) throws IOException {    String rememberMe = request.getParameter(JwtConstants.USER_LOGIN_REMEMBER_ME);    boolean isRememberMe = Boolean.parseBoolean(rememberMe);    User principal = (User) authentication.getPrincipal();    String token = JwtTokenUtils.createToken(principal, isRememberMe);    response.setContentType("application/json;charset=utf-8");    response.setHeader(JwtConstants.TOKEN_HEADER, token);    response.setStatus(HttpServletResponse.SC_OK);    PrintWriter responseWriter = response.getWriter();    responseWriter.write("{\"message\":\"登录成功\"}");    responseWriter.flush();    responseWriter.close();  }}
这段代码主要思路是——登录成功后,在authentication中获取已经认证成功的用户信息(user),然后将该user转换为token并返回给客户端。其中的isRememberMe是根据是否为true给予token不同的有效时间(查看完整源代码)。
3. JwtAuthorizationFilter.java(自定义基于JWT认证的过滤器)/** * JWT的权限过滤器. * * @author zuoyu * @program jwt * @create 2019-10-17 16:26 **/@Slf4jpublic class JwtAuthorizationFilter extends BasicAuthenticationFilter {  public JwtAuthorizationFilter(AuthenticationManager authenticationManager) {    super(authenticationManager);  }  @Override  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,      FilterChain chain) throws IOException, ServletException {    String token = request.getHeader(JwtConstants.TOKEN_HEADER);    if (StringUtils.isEmpty(token)) {      chain.doFilter(request, response);      return;    }    User user = JwtTokenUtils.getUserByToken(token);    Collection<!--? extends GrantedAuthority--> authorities = JwtTokenUtils.getAuthoritiesByToken(token);    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(        user, null, authorities);    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);    super.doFilterInternal(request, response, chain);  }}
这段代码的从请求中获取token,并将从token中解析出用户信息(user)和权限信息(authorities)。并根据用户信息(user)和权限信息(authorities)创建属于security框架的权限身份(authentication),将其存入当前的security环境。
五、使用方法如何使用——





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2