多系统多方式单点登入

SSO单点登录

SOO(Single Sign On) 单点登录

单点流程

cookie是用来在客户端存储数据的工具。
1、在其中一个子系统中登录,转到登录系统,在登录系统中完成登录,完成登录后向发起登录的子系统写入一个cookie
2、其他系统进行登录时,先判断是否存在token信息,存在及返回用户信息,没有统一到登陆地址页面进行登入,其他系统进行刷新即可
3、cookie中的子系统的域名,保持一致,所有的子系统才能访问到这个cookie

实现多系统单点登录
1、以springboot为项目背景
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>sso</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>sso-vip</module>
        <module>sso-main</module>
        <module>sso-cart</module>
        <module>sso-login</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath />
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring.boot.version>2.1.3.RELEASE</spring.boot.version>
        <junit.version>4.11</junit.version>
    </properties>

        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
                <version>${spring.boot.version}</version>
            </dependency>
            <!--热部署-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <optional>true</optional>
            </dependency>
            <!--测试包-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
            <!--lombok简写-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.20</version>

            </dependency>
            <!-- SpringBoot的依赖配置-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>${spring.boot.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-tomcat</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.3</version>
            </dependency>
            <!--druid 连接池-->
          <!--  <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.10</version>
            </dependency>-->
            <!--spring测试-->

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <scope>provided</scope>
            </dependency>
        </dependencies>

    <!--maven打包插件-->
</project>

2、修改host文件,刷新dns
#sso
127.0.0.1 www.codeshop.com
127.0.0.1 vip.codeshop.com
127.0.0.1 cart.codeshop.com
127.0.0.1 login.codeshop.com
3、创建父项目及多个子系统项目
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <parent>
    <artifactId>sso</artifactId>
    <groupId>org.example</groupId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>

  <artifactId>sso-cart</artifactId>
  <packaging>war</packaging>

  <name>sso-cart Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>

  </properties>

  <dependencies>


  </dependencies>

  <build>

  </build>
</project>

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <parent>
    <artifactId>sso</artifactId>
    <groupId>org.example</groupId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>


  <artifactId>sso-login</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>sso-login Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>

  </properties>

  <dependencies>
    <!--父依赖-->
    <!--<dependency>
      <groupId>org.example</groupId>
      <artifactId>sso</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>-->

  </dependencies>

  <build>

  </build>
</project>

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <parent>
    <artifactId>sso</artifactId>
    <groupId>org.example</groupId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>

  <artifactId>sso-main</artifactId>
  <packaging>war</packaging>

  <name>sso-main Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>

  </properties>

  <dependencies>


  </dependencies>

  <build>

  </build>
</project>

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <parent>
    <artifactId>sso</artifactId>
    <groupId>org.example</groupId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>

  <artifactId>sso-vip</artifactId>
  <packaging>war</packaging>

  <name>sso-vip Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>


  </dependencies>

  <build>

  </build>
</project>

3.1、启动类(单个)
package com.cn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

/**

 * @创建人  明

 */
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

3.2、配置(单个)
server:
  port: 9003
4、登录系统,负责token生成,写入。子系统登录页面拦截,负责db用户信息校验,登入用户信息退出
package com.cn.hm.controller;

import com.cn.hm.util.LogginCacheUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;

/*

 *@描述

 *@参数  视图控制

 *@返回值  

 *@创建人  HM

 *@创建时间  

 *@修改人和其它信息

 */
@Controller
@RequestMapping("/view")
public class ViewController {
    /**
    
     *@描述
    
     *@参数  跳转到登入页面
    
     *@返回值  
    
     *@创建人  HM
    
     *@创建时间  
    
     *@修改人和其它信息
    
     */
    @GetMapping("/login")
    public String toLogin(@RequestParam(required = false,defaultValue = "") String target
    , HttpSession session
    , @CookieValue(required = false,value = "TOKEN")Cookie cookie){
        if(StringUtils.isEmpty(target)){//主页地址
            target="http://www.codeshop.com:9002/view/index";
        }
        //进入登入系统,判断是否存在token
        if(cookie!=null && LogginCacheUtil.loginUser.get(cookie.getValue())!=null){
            return "redirect:"+target;
        }
        //地址校验
        //存入本次交互会话
        session.setAttribute("target",target);

        return "login";
    }
}
package com.cn.hm.controller;

import com.cn.hm.pojo.User;
import com.cn.hm.util.LogginCacheUtil;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;

/**

 *@描述

 *@参数  登录控制

 *@返回值  

 *@创建人  HM

 *@创建时间  

 *@修改人和其它信息

 */
@Controller
@RequestMapping("/login")
public class LoginController {
    //private static final Logger LOGGER= (Logger) LoggerFactory.getLogger(LoginController.class);
    private static Set<User> dbuser;
    static{
        dbuser=new HashSet<>();
        dbuser.add(new User(0,"zhangsan","123456"));
        dbuser.add(new User(1,"lisi","123456"));
        dbuser.add(new User(2,"wangwu","123456"));
        dbuser.add(new User(3,"hm","123456"));
    }
        @PostMapping
        public String doLogin(User user, HttpSession session, HttpServletResponse response){
            String target= (String) session.getAttribute("target");
            //判断用户是否存在
            Optional<User> first = dbuser.stream().filter(dbuser ->
                    dbuser.getPassword().equals(user.getPassword())
                            &&
                    dbuser.getUsername().equals(dbuser.getUsername())).findFirst();
            if(first.isPresent()){
                //保存用户信息
                String token = UUID.randomUUID().toString();
                Cookie cookie=new Cookie("TOKEN",token);
                cookie.setDomain("codeshop.com");
                cookie.setMaxAge(60);
                response.addCookie(cookie);
                LogginCacheUtil.loginUser.put(token,user);
            }else{
                //提示信息|返回当前页面
                session.setAttribute("msg","用户名或密码错误");
                return "login";
            }

            return "redirect:"+target;
        }

    /*
    
     *@描述
    
     *@参数  根据token返回用户信息
    
     *@返回值  
    
     *@创建人  HM
    
     *@创建时间  
    
     *@修改人和其它信息
    
     */    
    @GetMapping("/info")
    @ResponseBody
    public ResponseEntity<User> getUserInfo( String token){

          if(!StringUtils.isEmpty(token)){
              return ResponseEntity.ok(LogginCacheUtil.loginUser.get(token));
          }else{
              return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
          }
        }
}

6、子系统负责检查是否存在token,没有重定向到登入系统页面,地址校验
package com.cn.hm.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Map;

@Controller
@RequestMapping("/view")
public class IndexController {
   /* private static  final Logger LOGGER= (Logger) LoggerFactory.getLogger(IndexController.class);*/


    @Autowired
    private RestTemplate testTemplate;

    private static String LOGIN_INFO_ADDRESS="http://login.codeshop.com:9001/login/info?token=";


    @GetMapping("/index")
    public String toIndex(@CookieValue(required = false,value = "TOKEN")Cookie cookie, HttpSession session){
        if(cookie != null && !StringUtils.isEmpty(cookie.getValue())){
            System.out.println("result"+LOGIN_INFO_ADDRESS);
            Map result = testTemplate.getForObject(LOGIN_INFO_ADDRESS+cookie.getValue(), Map.class);
            //LOGGER.info("认证信息"+result);
            System.out.println("result"+result);
            session.setAttribute("logginUser",result);
        }
        return "index";
    }
    @GetMapping("/quit")
    public String toLoggin(@CookieValue(required = false,value = "TOKEN")Cookie cookie,
                           HttpServletResponse response,
                           HttpSession session){
        if(cookie != null && !StringUtils.isEmpty(cookie.getValue())){
            //单位为秒
            cookie.setMaxAge(0);
            response.addCookie(cookie);
            session.removeAttribute("logginUser");

        }
        return "index";
    }
}


JWT单点登录

jwt官网

什么是jwt?

JSON Web Token (JWT) 是一个开放标准 ( RFC 7519 ),它定义了一种紧凑且自包含的方式,用于在各方之间作为 JSON 对象安全地传输信息。

场景

授权:这是使用 JWT 最常见的场景。用户登录后,每个后续请求都将包含 JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小,并且能够轻松跨不同域使用。
信息交换:JSON Web Tokens 是一种在各方之间安全传输信息的好方法。因为 JWT 可以被签名——例如,使用公钥/私钥对——你可以确定发件人就是他们所说的那样。此外,由于使用标头和有效负载计算签名,因此您还可以验证内容是否未被篡改。

与传统的session认证有什么区别?

1、传统验证,数据量大是会消耗服务端内存
2、 基于cookie来识别用户,容易受到跨站伪造攻击, 不利于系统安全
3、在分布式系统中,不利于信息共享和集群应用,需要额外第三方工具做session信息共享

jwt认证流程

1、通过web表单将自己的用户名和密码发送到后端的接口,post请求,建议通过ssl加密的传输
2、后端对用户名和密码进行验证成功后,将用户的id等其他信息作为jwt payload(负载),将其头部分别进行base64编码拼接后签名,形成一个jwt(token)
。形成的jwt就是一个形同11.zz.xx的字符串。三部分:头.负载.签名组成,中间由点进行隔开
3、后端将jwt字符串作为登录成功后的结果返回给前端。前端将返回后的结果保存在localstorage或sessionStorage上,退出登录时将前端保存的jwt删除即可。
4、前端在每次请求时将jwt放入http head中的authorization位。(解决xss和xsrf问题)
5、后端检查是否存在,如存在验证jwt的有效性。例如签名是否正确;检查token是否过期;检查token的接收方是否是自己
6、验证通过后使用jwt中包含的用户信息进行其他逻辑操作,返回相应的结果。

优势

1、简洁(compact):可以通过url,post参数或者http headeer发送,数据量小,传输数独快
2、自包含:负载中包含了所有用户所需要的信息,避免多次数据库查询
3、token是以json加密的形式保存在客户端的,所以jwt是跨语言的
4、不需要在服务端保存会话信息,特别适用于分布式微服务

jwt的结构是什么?

令牌组成
1、标头(header)
2、有效负荷(payload)
3、签名(Signature)
header.payload.Signature
分析:
header:有两部分组成:令牌的类型和所使用的签名算法.例如HMAC SHA256或RSA.使用base64编码成jwt结构的第一部分。不允许在负载中加入敏感信息
BASE64UrlEncode(header):
{
“alg”:“HS256”,
“typ”:“jwt”
}
payload:第二部分有效负载:声明有关用户和其他数据的声明。同样用base64编码组成jwt结构的第二部分.不允许在负载中加入敏感信息
BASE64UrlEncode(payload):
{
“user_id”:“ajsdjfklasjdkljflkasjd213134kjfkaljsdlkfj”,
“user_name”:“hm”,
“domin”:true
}
signature:第三部分由前两部分base64编码后+自定义的随机密钥组成。HS256签名的作用是保证jwt没有被篡改过。
HMACSHA256(BASE64UrlEncode(header)+"."+BASE64UrlEncode(header),secret);
例如:BASE64UrlEncode(header)+"."+BASE64UrlEncode(payload)+“.”+HMACSHA256(BASE64UrlEncode(header)+"."+BASE64UrlEncode(payload),secret)

jwt常现异常信息
signatureverificationException:  签名不一致异常
TokenecpiredException:  令牌过期异常
algorithmMismatchException:   算法不匹配异常
Incalidclaimexception:   失效的payload异常
功能实例

在这里插入图片描述

在这里插入图片描述

需求小故事:

1、实现登入成功token生成,2、生成token后,登入前的token验证返回用户信息

配置信息-application.properties
# 应用名称
spring.application.name=jwt
spring.http.converters.preferred-json-mapper=gson
# 数据库信息
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/jwt?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

mybatis.type-aliases-package=com.example.jwt.entity
mybatis.mapper-locations=classpath:com/cn/jwt/mapper/*.xml
logging.level.com.example.jwt.dao=debug

# 应用服务 WEB 访问端口
server.port=8080
# THYMELEAF (ThymeleafAutoConfiguration)
# 开启模板缓存(默认值: true )
spring.thymeleaf.cache=true
# 检查模板是否存在,然后再呈现
spring.thymeleaf.check-template=true
# 检查模板位置是否正确(默认值 :true )
spring.thymeleaf.check-template-location=true
#Content-Type 的值(默认值: text/html )
spring.thymeleaf.content-type=text/html
# 开启 MVC Thymeleaf 视图解析(默认值: true )
spring.thymeleaf.enabled=true
# 模板编码
spring.thymeleaf.encoding=UTF-8
# 要被排除在解析之外的视图名称列表,⽤逗号分隔
spring.thymeleaf.excluded-view-names=
# 要运⽤于模板之上的模板模式。另⻅ StandardTemplate-ModeHandlers( 默认值: HTML5)
spring.thymeleaf.mode=HTML5
# 在构建 URL 时添加到视图名称前的前缀(默认值: classpath:/templates/ )
spring.thymeleaf.prefix=classpath:/templates/
# 在构建 URL 时添加到视图名称后的后缀(默认值: .html )
spring.thymeleaf.suffix=.html

pom.xml - jar包
<?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>
    <groupId>com.example</groupId>
    <artifactId>jwt</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>jwt</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.1.18.RELEASE</spring-boot.version>
        <spring-cloud.version>Greenwich.SR6</spring-cloud.version>
    </properties>

    <dependencies>
        <!-- slf4j-log4j12 -->
        <!-- <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-log4j12</artifactId>
             <version>1.7.30</version>
             <scope>test</scope>
         </dependency>-->
        <!--jwt包-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.7.0</version>
        </dependency>
        <!--mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>
        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
        <!--模板插件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!--web依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--web服务-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web-services</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>


        <!--测试包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <!--打包插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.1.18.RELEASE</version>
                <configuration>
                    <mainClass>com.example.jwt.JwtApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.jwt.dao.UserDao">
    <select id="login" parameterType="User" resultType="User">
            select  * from user
            where    userName = #{userName} and   userPassword  = #{userPassword}

    </select>
</mapper>

dao
package com.example.jwt.dao;

import com.example.jwt.entity.User;
import org.apache.ibatis.annotations.Mapper;

/**

 *@描述

 *@参数

 *@返回值

 *@创建人  HM

 *@创建时间

 *@修改人和其它信息

 */

@Mapper
public interface UserDao {
    User login(User user);
}

services
package com.example.jwt.service;

import com.example.jwt.entity.User;

/**

 *@描述

 *@参数  用户验证业务接口

 *@返回值

 *@创建人  HM

 *@创建时间

 *@修改人和其它信息

 */
public interface UserService {

     User login(User user);

}
----
package com.example.jwt.service.impl;

import com.example.jwt.dao.UserDao;
import com.example.jwt.entity.User;
import com.example.jwt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

/**

 *@描述

 *@参数  登入业务验证

 *@返回值

 *@创建人  HM

 *@创建时间

 *@修改人和其它信息

 */
@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public User login(User user) {
        User userVal = userDao.login(user);
        if(!StringUtils.isEmpty(userVal)){
            return userVal;
        }
        throw  new RuntimeException("认证失败---");
    }
}

controller
package com.example.jwt.controller;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.jwt.entity.User;
import com.example.jwt.service.UserService;
import com.example.jwt.util.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
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.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@RestController
@Slf4j
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping("/user/login")
    public Map<String,Object> toIndex(User user){
        log.info("用户信息"+user.getUserName()+":"+user.getUserPassword());
        Map<String,Object> map = new HashMap<>();
        try{
            User login = userService.login(user);
            Map<String,String> payload= new HashMap<>();
            payload.put("userId",login.getUserId());
            payload.put("userName",login.getUserName());
            //生成token
            String token = JWTUtils.getToken(payload);
            map.put("state",true);
            map.put("msg","认证成功");
            map.put("token",token);//存到客户端缓存 LocalStorage | SessionStorage
        }catch (Exception e){
            map.put("state",false);
            map.put("msg",e.getMessage());
        }
        return map;
    }

    /**
    
     *@描述
    
     *@参数  token验证在进行登入验证
    
     *@返回值  
    
     *@创建人  HM
    
     *@创建时间  
    
     *@修改人和其它信息
    
     */
    @PostMapping(name = "/user/valaToken")
    public Map<String,Object> validateToken(HttpServletRequest request){
        String token = request.getHeader("token");
        DecodedJWT tokenInfo = JWTUtils.getTokenInfo(token);
        Map<String,Object> map= new HashMap<>();
        map.put("用户信息",tokenInfo.getClaim("name").asString());
        log.info("当前token",token);
        map.put("state",true);
        map.put("msg","请求成功!");
        return map;
    }
}


interceptors
package com.example.jwt.interceptors;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.jwt.util.JWTUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**

 *@描述

 *@参数  jwt拦截器 (token验证)

 *@返回值  

 *@创建人  HM

 *@创建时间  

 *@修改人和其它信息

 */
public class JWTInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HashMap<String, Object> map = new HashMap<>();
        String token = request.getHeader("token");
        try {
             JWTUtils.verify(token);
            return true;
        }catch (SignatureVerificationException e){
            e.printStackTrace();
            map.put("msg","无效签名");
        }catch (TokenExpiredException e){
            e.printStackTrace();
            map.put("msg","token过期");
        }catch (AlgorithmMismatchException e){
            e.printStackTrace();
            map.put("msg","token算法不一致");
        }catch (Exception e){
            e.printStackTrace();
            map.put("msg","无效签名");
        }
        map.put("state",false);
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
        return false;
    }
}


config
package com.example.jwt.config;

import com.example.jwt.interceptors.JWTInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**

 *@描述

 *@参数  注册拦截器

 *@返回值  

 *@创建人  HM

 *@创建时间  

 *@修改人和其它信息

 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/user/valaToken")//拦截
                .excludePathPatterns("/user/login");//放行
    }
}
entity
package com.example.jwt.entity;

import java.io.Serializable;

/**

 *@author Administrator
 * @描述

 *@参数  验证实体

 *@返回值

 *@创建人  HM

 *@创建时间

 *@修改人和其它信息

 */
public class User implements Serializable {

    private String userId;
    private String userName;
    private String userPassword;
    User(){

    }
    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserPassword() {
        return userPassword;
    }

    public void setUserPassword(String userPassword) {
        this.userPassword = userPassword;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId='" + userId + '\'' +
                ", userName='" + userName + '\'' +
                ", userPassword='" + userPassword + '\'' +
                '}';
    }
}
utils
package com.example.jwt.util;


import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**

 *@描述

 *@参数  token信息逻辑

 *@返回值  

 *@创建人  HM

 *@创建时间  

 *@修改人和其它信息

 */
public class JWTUtils {
    private static String SING = "token!Q@W3e4r";
    public static final long EXPIRE_DATE=259200000;//1000*60*60*24*3

    /**
     * 生成token
     * @param map  //传入payload
     * @return 返回token
     */
    public static String getToken(Map<String,String> map){
        if(StringUtils.isEmpty(map)){
            throw  new RuntimeException("用户载入信息失败");
        }
        JWTCreator.Builder builder = JWT.create();
        map.forEach((k,v)->{
            builder.withClaim(k,v);
        });
        //过期时间
        builder.withExpiresAt(new Date(System.currentTimeMillis()+EXPIRE_DATE))
         //签发人
        .withIssuer("hm")
       //签发时间
        .withIssuedAt(new Date());
        return builder.sign(Algorithm.HMAC256(SING));
    }
    /**
     * 验证token // 如果验证通过,则不会报错,否则会抛出异常
     * @param token
     * @return
     */
    public static DecodedJWT verify(String token){
        if(StringUtils.isEmpty(token)){
            throw  new RuntimeException("token验证失败1:"+token);
        }
        try {
            return  JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
        }catch (Exception e){
           throw  new RuntimeException("token验证失败2:"+e.getMessage());
            //..处理视图

        }
    }
    /**
     * 获取token中payload
     * @param token
     * @return
     */
    public static DecodedJWT getTokenInfo(String token){
        if(StringUtils.isEmpty(token)){
            throw  new RuntimeException("获取token信息:"+token);
        }
        return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
    }

    @Test
    public void testUtil(){
        Map<String,String> map=new HashMap<String,String>();
        //存放的信息流
        map.put("userName","hm");
        map.put("userAccount","1343876419");
        String token = JWTUtils.getToken(map);
        //生成的token
        System.out.println(token);
        //eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
        // eyJ1c2VyQWNjb3VudCI6IjEzNDM4NzY0MTkiLCJ1c2VyTmFtZSI6ImhtIiwiZXhwIjoxNjIzMjUwOTA4fQ.
        // lyQKtiO7-UIEPSIM8ch6Tb6YO7MmolntG1zmFYhbZ0o
        //取编码后的token
        //"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImhtIiwiZXhwIjoxNjIzNDkzNjkyLCJ1c2VySWQiOiIxMjMxMmpzaGFzZGprZmFzZCJ9.Kx6K7NwIzfKlE0ZXlaE1yH2MrrxDuz5FBBhxjv-jx44");
        DecodedJWT verify = JWTUtils.getTokenInfo(token);
        //获取单个的jwt中的信息
        System.out.println(">>>"+verify.getClaims().get("userName").asString());
       //token验证
        DecodedJWT verifyInfoFlag = JWTUtils.verify(token);
        if(!StringUtils.isEmpty(verifyInfoFlag)){
            System.out.println("验证通过");
        }else{
            System.out.println("验证失败");
        }
        /*DecodedJWT verifyInfoFlag = JWTUtils.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImhtIiwiZXhwIjoxNjIzNDkzNjkyLCJ1c2VySWQiOiIxMjMxMmpzaGFzZGprZmFzZCJ9.Kx6K7NwIzfKlE0ZXlaE1yH2MrrxDuz5FBBhxjv-jx44");
        if(!StringUtils.isEmpty(verifyInfoFlag)){
            System.out.println("验证通过");
        }else{
            System.out.println("验证失败");
        }*/
    }
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知青先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值