SpringSecurity是大名鼎鼎的认证授权框架,在SSH时代就已经大放异彩。在JAVA项目中,权限框架的解决方案主要是以SpringSecurity和Shiro为主。JWT是目前主流的基于access-token的的认证框架,在项目中一般时SpringSecurity和JWT结合使用,在前后端分离的架构中,使用JWT作为认证框架更为普遍。
本文介绍在SpringBoot中整合SpringSecurity和JWT框架的完整流程。
一、增加依赖包
首先在openjweb-core工程的pom.xml里增加SpringSecurity相关的依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--这个代码实现中会用到--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
另外后面的示例代码中会用到jwt、HuTool工具类库等,放到openjweb-common公共模块包,这些包基本是大部分Java项目开发中都要用到的,把下面的依赖加到openjweb-common的pom.xml:
<!-- jwt --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency><dependency><groupId>com.github.axet</groupId><artifactId>kaptcha</artifactId><version>0.0.9</version></dependency><!-- hutool工具类--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.3</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version> <!--3.11 is error--></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.15</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>
下面我们先看下整合到这一步运行的效果,启动openjweb-sys的应用,然后访问我们在前面章节介绍的随便一个接口,例如: http://localhost:8001/api/cms/cmsInfo/queryCom,我们会发现请求定向到了一个登录页面,这个登录页面是Spring Security的默认登录页面:
此时我们还没配置用户角色等,那么这个界面输入什么口令登录,就是Spring Security提供的默认的登录账号和密码,这个用户名和密码是什么呢?不是admin和123456,密码是SpringBoot启动的时候,控制台中生成的一个密码,看启动界面:
在登录界面中输入用户名user (不是admin),密码是上图标红的密码,输入成功后,会调用访问接口返回的执行结果:
在实际项目开发中,我们肯定要设计我们自己的用户、角色、权限、角色权限关系表。所以我们后面需要设计相关的数据库表。接下来我们需要实现的代码比较多。可以按下面的步骤开发。
二、Jwt工具类
首先我们开发一个Jwt工具类,用于登录时将登录账号生成对应的access_token,以及根据access_token反向解析出登录账号。
在开发Jwt工具类之前,先在openjweb-sys的pom.xml中配置几个jwt需要用到的变量。变量在控制层通过@Value注解引入,所以需要先在openjweb-sys的pom.xml中增加一个依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency>
先开发一个Jwt工具类,用来生成Jwt,解析Jwt,判断Jwt是否过期,Jwt工具类可以放到openjweb-core下面的org.openjweb.core.util下,见下面的代码:
package org.openjweb.core.util;import io.jsonwebtoken.*;
import lombok.Data;
import java.util.Date;@Data
public class JwtUtil {private long expire;private String secret;private String header;public JwtUtil(){}/**** @param header 头部* @param secert 加密私钥* @param expire 过期秒数*/public JwtUtil(String header,String secert,long expire){this.expire = expire;this.header = header;this.secret = secert;}// 生成JWTpublic String generateToken(String username) {Date nowDate = new Date();Date expireDate = new Date(nowDate.getTime() + 1000 * expire);return Jwts.builder().setHeaderParam("typ", "JWT").setSubject(username).setIssuedAt(nowDate).setExpiration(expireDate) // 过期时间.signWith(SignatureAlgorithm.HS512, secret).compact();}// 解析JWTpublic Claims getClaimsByToken(String jwt) {try {return Jwts.parser().setSigningKey(secret).parseClaimsJws(jwt).getBody();} catch (Exception e) {return null;}}// 判断JWT是否过期public boolean isTokenExpired(Claims claims) {return claims.getExpiration().before(new Date());}}
然后在openjweb-sys的application-dev.yml增加jwt的配置,包括header(设置认证方式)、secret加密密钥、expire过期时间。见下面配置:
然后我们开发一个API接口类用于jwt的测试,在openjweb-b2b2c的org.openjweb.b2c.api下创建RedisDemoApi,至于为什么在b2c模块中建这个测试类,是为了演示在其他子模块中也能读取到openjweb-sys的application-dev.yml中的配置:
package org.openjweb.b2c.api;import lombok.extern.slf4j.Slf4j;
import org.openjweb.common.response.ResponseResult;
import org.openjweb.core.util.JwtUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/api/jwt/")
@Slf4j
public class JwtDemoApi {@Value("${jwt.expire}")private long expire;@Value("${jwt.secret}")private String secret;@Value("${jwt.header}")private String header;localhost:8001/api/jwt/testjwt?loginId=admin@RequestMapping("/testjwt")public ResponseResult testJwt(String loginId) {String str = "";log.info("开始调用testjwt::");log.info("配置里读header");log.info(header);log.info(secret);log.info(String.valueOf(expire));JwtUtil jwtUtil = new JwtUtil(header, secret, expire);str = jwtUtil.generateToken(loginId);//检查是否读取了配置文件的参数log.info("测试账号转jwt:");log.info(str);return ResponseResult.okResult(str);}
}
上面代码中,通过@Value注解从application-dev.yml中获取配置的值,在测试方法testJwt中,通过log.info输入的信息可以看到读取的内容,然后通过newJwtUtil构造函数对登录账号进行加密,然后输入加密后的accessToken。下面是测试地址:localhost:8001/api/jwt/testjwt?loginId=admin
测试后的界面输出:
data是返回的access_token。那么如何从access_token中解析用户账号呢?使用下面的代码解析登录账号:
String loginId = jwtUtil.getClaimsByToken(str).getSubject();
由于篇幅比较长,本文先介绍到这里,后面再增加2篇文章接续介绍SpringSecurity+JWT 整合。
本文的示例代码见Github: https://github.com/openjweb/cloud/tree/masterhttps://github.com/openjweb/cloud/tree/master