zoukankan      html  css  js  c++  java
  • springboot 添加JWT接口认证

      注:此认证方式生成token基本jdk8.0, jdk其它版本有些方法不支持

      pom.xml文件添加引用

     <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>0.9.1</version>
            </dependency>

      完整pom文件

    <?xml version="1.0" encoding="utf-8"?>
    <project
            xmlns="http://maven.apache.org/POM/4.0.0"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.2.RELEASE</version>
            <relativePath/>
            <!-- lookup parent from repository -->
        </parent>
        <groupId>com.xj</groupId>
        <artifactId>demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>demo</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
            <kotlin.version>1.4.0</kotlin.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.1</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>3.0.0</version>
                <scope>compile</scope>
            </dependency>
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>0.9.1</version>
            </dependency>
            <!-- SLf4j 日志记录-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
                <version>1.18.12</version>
            </dependency>
            <!--JSONObject-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.73</version>
            </dependency>
            <!-- JSONObject对象依赖的jar包 开始 -->
            <dependency>
                <groupId>commons-beanutils</groupId>
                <artifactId>commons-beanutils</artifactId>
                <version>1.9.3</version>
            </dependency>
            <dependency>
                <groupId>commons-collections</groupId>
                <artifactId>commons-collections</artifactId>
                <version>3.2.1</version>
            </dependency>
            <dependency>
                <groupId>commons-lang</groupId>
                <artifactId>commons-lang</artifactId>
                <version>2.6</version>
            </dependency>
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.1.1</version>
            </dependency>
            <dependency>
                <groupId>net.sf.ezmorph</groupId>
                <artifactId>ezmorph</artifactId>
                <version>1.0.6</version>
            </dependency>
            <dependency>
                <groupId>net.sf.json-lib</groupId>
                <artifactId>json-lib</artifactId>
                <version>2.2.3</version>
                <classifier>jdk15</classifier>
                <!-- jdk版本 -->
            </dependency>
            <!-- Json依赖架包下载结束 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.7</version>
            </dependency>
            <!-- swagger3.0 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-boot-starter</artifactId>
                <version>3.0.0</version>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <mainClass>com.xj.demo.DemoApplication</mainClass>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>2.6</version>
                    <configuration>
                        <archive>
                            <manifest>
                                <addClasspath>true</addClasspath>
                                <classpathPrefix>lib/</classpathPrefix>
                                <mainClass>com.xj.demo.DemoApplication</mainClass>
                            </manifest>
                        </archive>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <version>2.10</version>
                    <executions>
                        <execution>
                            <id>copy-dependencies</id>
                            <phase>package</phase>
                            <goals>
                                <goal>copy-dependencies</goal>
                            </goals>
                            <configuration>
                                <outputDirectory>${project.build.directory}/config</outputDirectory>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
    View Code

       jwt密钥配置 application.properties

    #基本配置
    spring.datasource.url=jdbc:mysql://127.0.0.1:3306/business?useUnicode=true&characterEncoding=utf-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
    spring.datasource.username=root
    spring.datasource.password=root
    spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
    #使用mysql
    spring.jpa.database = mysql
    #是否显示sql语句
    spring.jpa.show-sql=true
    #mybatis配置 mybatis.config-location=classpath:mybatis-config.xml // 配置文件位置
    mybatis.typeAliasesPackage=com.xj.demo.model 
    mybatis.mapper-locations=classpath:mapper/*.xml
    
    
    #代表这个JWT的接收对象,存入audience
    audience.clientId=098f6bcd4621d373cade4e832627b4f6
    # 密钥, 经过Base64加密, 可自行替换
    audience.base64Secret=MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY=
    #JWT的签发主体,存入issuer
    audience.name=springbootapi
    # 过期时间,48小时过期
    audience.expiresSecond= 172800
    
    #配置mybaits自定义类型转换类所在的包
    # mybatis.type-handlers-package=com.hl.handler
    View Code
     JWTToken类
    package com.xj.demo.common;
    
    import com.xj.demo.common.exception.CustomException;
    import com.xj.demo.common.response.ResultCode;
    import com.xj.demo.config.Audience;
    import io.jsonwebtoken.*;
    import org.apache.logging.log4j.util.Base64Util;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import javax.crypto.spec.SecretKeySpec;
    import javax.xml.bind.DatatypeConverter;
    import java.security.Key;
    import java.util.Date;
    
    /**
     * JWTToken类
     */
    public class JwtTokenUtil {
    
        private static Logger log = LoggerFactory.getLogger(JwtTokenUtil.class);
    
        public static final String AUTH_HEADER_KEY = "Authorization";
    
        public static final String TOKEN_PREFIX = "Bearer ";
    
        /**
         * 解析jwt
         * @param jsonWebToken
         * @param base64Security
         * @return
         */
        public static Claims parseJWT(String jsonWebToken, String base64Security) {
            try {
                Claims claims = Jwts.parser()
                        .setSigningKey(DatatypeConverter.parseBase64Binary(base64Security))
                        .parseClaimsJws(jsonWebToken).getBody();
                return claims;
            } catch (ExpiredJwtException  eje) {
                log.error("===== Token过期 =====", eje);
                throw new CustomException(ResultCode.PERMISSION_TOKEN_EXPIRED);
            } catch (Exception e){
                log.error("===== token解析异常 =====", e);
                throw new CustomException(ResultCode.PERMISSION_TOKEN_INVALID);
            }
        }
    
        /**
         * 构建jwt
         * @param userId
         * @param username
         * @param role
         * @param audience
         * @return
         */
        public static String createJWT(String userId, String username, String role, Audience audience) {
            try {
                // 使用HS256加密算法
                SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
    
                long nowMillis = System.currentTimeMillis();
                Date now = new Date(nowMillis);
    
                //生成签名密钥
                byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(audience.getBase64Secret());
                Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
    
                //userId是重要信息,进行加密下
                String encryId = Base64Util.encode(userId);
    
                //添加构成JWT的参数
                JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")
                        // 可以将基本不重要的对象信息放到claims
                        .claim("role", role)
                        .claim("userId", userId)
                        .setSubject(username)           // 代表这个JWT的主体,即它的所有人
                        .setIssuer(audience.getClientId())              // 代表这个JWT的签发主体;
                        .setIssuedAt(new Date())        // 是一个时间戳,代表这个JWT的签发时间;
                        .setAudience(audience.getName())          // 代表这个JWT的接收对象;
                        .signWith(signatureAlgorithm, signingKey);
                //添加Token过期时间
                int TTLMillis = audience.getExpiresSecond();
                if (TTLMillis >= 0) {
                    long expMillis = nowMillis + TTLMillis;
                    Date exp = new Date(expMillis);
                    builder.setExpiration(exp)  // 是一个时间戳,代表这个JWT的过期时间;
                            .setNotBefore(now); // 是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的
                }
    
                //生成JWT
                return builder.compact();
            } catch (Exception e) {
                log.error("签名失败", e);
                throw new CustomException(ResultCode.PERMISSION_SIGNATURE_ERROR);
            }
        }
    
        /**
         * 从token中获取用户名
         * @param token
         * @param base64Security
         * @return
         */
        public static String getUsername(String token, String base64Security){
            return parseJWT(token, base64Security).getSubject();
        }
    
        /**
         * 从token中获取用户ID
         * @param token
         * @param base64Security
         * @return
         */
        public static String getUserId(String token, String base64Security){
            String userId = parseJWT(token, base64Security).get("userId", String.class);
            //return Base64Util.decode(userId);
            return  userId;
        }
    
        /**
         * 是否已过期
         * @param token
         * @param base64Security
         * @return
         */
        public static boolean isExpiration(String token, String base64Security) {
            return parseJWT(token, base64Security).getExpiration().before(new Date());
        }
    }
    View Code

      

     token验证拦截器(filter)
    package com.xj.demo.filter;
    
    
    import com.xj.demo.annotation.JwtIgnore;
    import com.xj.demo.common.JwtTokenUtil;
    import com.xj.demo.common.exception.CustomException;
    import com.xj.demo.common.response.ResultCode;
    import com.xj.demo.config.Audience;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpMethod;
    import org.springframework.web.context.support.WebApplicationContextUtils;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    
    /**
     * ========================
     * token验证拦截器
     * Created with IntelliJ IDEA.
     * User:xj
     * Date:2020/8/18
     * Version: v1.0
     * ========================
     */
    
    @Slf4j
    //@WebFilter(urlPatterns = "/filter-api/*", filterName = "jwkTokenFilter")
    public class JwtInterceptor extends HandlerInterceptorAdapter {
    
        @Autowired
        private Audience audience;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 忽略带JwtIgnore注解的请求, 不做后续token认证校验
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                JwtIgnore jwtIgnore = handlerMethod.getMethodAnnotation(JwtIgnore.class);
                if (jwtIgnore != null) {
                    return true;
                }
            }
    
            if (HttpMethod.OPTIONS.equals(request.getMethod())) {
                response.setStatus(HttpServletResponse.SC_OK);
                return true;
            }
    
            // 获取请求头信息authorization信息
            final String authHeader = request.getHeader(JwtTokenUtil.AUTH_HEADER_KEY);
            log.info("## authHeader= {}", authHeader);
    
            if (StringUtils.isBlank(authHeader) || !authHeader.startsWith(JwtTokenUtil.TOKEN_PREFIX)) {
                log.info("### 用户未登录,请先登录 ###");
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                throw new CustomException(ResultCode.USER_NOT_LOGGED_IN);
            }
    
            // 获取token
            final String token = authHeader.substring(7);
    
            if(audience == null){
                BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
                audience = (Audience) factory.getBean("audience");
            }
            try {
            // 验证token是否有效--无效已做异常抛出,由全局异常处理后返回对应信息
            JwtTokenUtil.parseJWT(token, audience.getBase64Secret());
            }
            catch (Exception e){
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                throw new CustomException(ResultCode.USER_NOT_LOGGED_IN);
            }
            return true;
        }
    
    }
    View Code

      

    JWT验证忽略注解
    package com.xj.demo.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * ========================
     * JWT验证忽略注解
     * Created with IntelliJ IDEA.
     * User:xj
     * Date:2020/8/18 9:50
     * Version: v1.0
     * ========================
     */
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface JwtIgnore {
    }
    View Code

      

    自定义异常类型
    package com.xj.demo.common.exception;
    
    
    
    import com.xj.demo.common.response.ResultCode;
    
    import java.text.MessageFormat;
    
    /**
     * 自定义异常类型
     * @author xj
     **/
    public class CustomException extends RuntimeException {
    
        //错误代码
        ResultCode resultCode;
    
        public CustomException(ResultCode resultCode){
            super(resultCode.message());
            this.resultCode = resultCode;
        }
    
        public CustomException(ResultCode resultCode, Object... args){
            super(resultCode.message());
            String message = MessageFormat.format(resultCode.message(), args);
            resultCode.setMessage(message);
            this.resultCode = resultCode;
        }
    
        public ResultCode getResultCode(){
            return resultCode;
        }
    
    }
    View Code
    统一响应结果集
    package com.xj.demo.common.response;
    
    
    import com.fasterxml.jackson.databind.annotation.JsonSerialize;
    
    /**
     * ========================
     * 统一响应结果集
     * Created with IntelliJ IDEA.
     * User:xj
     * Date:2020/8/18 9:50
     * Version: v1.0
     * ========================
     */
    @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
    public class Result<T> {
    
        //操作代码
        int code;
    
        //提示信息
        String message;
    
        //结果数据
        T data;
    
        public Result(ResultCode resultCode){
            this.code = resultCode.code();
            this.message = resultCode.message();
        }
    
        public Result(ResultCode resultCode, T data){
            this.code = resultCode.code();
            this.message = resultCode.message();
            this.data = data;
        }
    
        public Result(String message){
            this.message = message;
        }
    
        public static Result SUCCESS(){
            return new Result(ResultCode.SUCCESS);
        }
    
        public static <T> Result SUCCESS(T data){
            return new Result(ResultCode.SUCCESS, data);
        }
    
        public static Result FAIL(){
            return new Result(ResultCode.FAIL);
        }
    
        public static Result FAIL(String message){
            return new Result(message);
        }
    
        public int getCode() {
            return code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    }
    View Code
    package com.xj.demo.common.response;
    
    /**
     * ========================
     * 通用响应状态
     * Created with IntelliJ IDEA.
     * User:xj
     * Date:2020/8/18
     * Time:10:10
     * Version: v1.0
     * ========================
     */
    public enum  ResultCode {
    
        /* 成功状态码 */
        SUCCESS(1,"操作成功!"),
    
        /* 错误状态码 */
        FAIL(-1,"操作失败!"),
    
        /* 参数错误:10001-19999 */
        PARAM_IS_INVALID(10001, "参数无效"),
        PARAM_IS_BLANK(10002, "参数为空"),
        PARAM_TYPE_BIND_ERROR(10003, "参数格式错误"),
        PARAM_NOT_COMPLETE(10004, "参数缺失"),
    
        /* 用户错误:20001-29999*/
        USER_NOT_LOGGED_IN(20001, "用户未登录,请先登录"),
        USER_LOGIN_ERROR(20002, "账号不存在或密码错误"),
        USER_ACCOUNT_FORBIDDEN(20003, "账号已被禁用"),
        USER_NOT_EXIST(20004, "用户不存在"),
        USER_HAS_EXISTED(20005, "用户已存在"),
    
    
        /* 数据错误:50001-599999 */
        RESULT_DATA_NONE(50001, "数据未找到"),
        DATA_IS_WRONG(50002, "数据有误"),
        DATA_ALREADY_EXISTED(50003, "数据已存在"),
    
        /* 权限错误:70001-79999 */
        PERMISSION_UNAUTHENTICATED(70001,"此操作需要登陆系统!"),
        PERMISSION_UNAUTHORISE(70002,"权限不足,无权操作!"),
        PERMISSION_EXPIRE(70003,"登录状态过期!"),
        PERMISSION_TOKEN_EXPIRED(70004, "token已过期"),
        PERMISSION_LIMIT(70005, "访问次数受限制"),
        PERMISSION_TOKEN_INVALID(70006, "无效token"),
        PERMISSION_SIGNATURE_ERROR(70007, "签名失败");
    
        //操作代码
        int code;
    
        public int code() {
            return code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    
        public String message() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
    
    
        //提示信息
        String message;
        ResultCode(int code, String message){
            this.code = code;
            this.message = message;
        }
    }
    View Code

     登录接口

    package com.xj.demo.controller;
    
    import com.alibaba.fastjson.JSONObject;
    import com.fasterxml.jackson.databind.util.JSONPObject;
    import com.xj.demo.annotation.JwtIgnore;
    import com.xj.demo.common.JwtTokenUtil;
    import com.xj.demo.common.response.Result;
    import com.xj.demo.config.Audience;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.jackson.JsonObjectDeserializer;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.servlet.http.HttpServletResponse;
    import java.util.UUID;
    
    @Api(tags = "登录")
    @RestController
    @RequestMapping("/login")
    @Slf4j
    public class LoginController {
        @Autowired
        private Audience audience;
    
        @ApiOperation("登录")
        @PostMapping("/login")
        @JwtIgnore
        public Result adminLogin(HttpServletResponse response, String username, String password) {
            // 这里模拟测试, 默认登录成功,返回用户ID和角色信息
            String userId = UUID.randomUUID().toString();
            String role = "admin";
    
            // 创建token
            String token = JwtTokenUtil.createJWT(userId, username, role, audience);
            log.info("### 登录成功, token={} ###", token);
    
            // 将token放在响应头
            response.setHeader(JwtTokenUtil.AUTH_HEADER_KEY, JwtTokenUtil.TOKEN_PREFIX + token);
            return Result.SUCCESS(token);
        }
    }
    View Code

     验证登录截图

      源码下载地址:https://download.csdn.net/download/haojuntu/12805887

  • 相关阅读:
    如何更改VS2005调试网站的浏览器类型
    StringBuilder 的 Capacity属性
    Convert.ToInt32,Int32.Parse和Int32.TryParse的关系
    今天第一天注册
    关于Random产生随机数测试
    [导入]Reporting Services 4: Web Service
    [导入]Reporting Services 5: Extensions & Custom Report Item
    silverlight缓存无法更新的简易解决办法
    总结前段时间做的电话业务故障处理系统(1)
    atlas
  • 原文地址:https://www.cnblogs.com/personblog/p/13608969.html
Copyright © 2011-2022 走看看