JWT 单点登入

Maven  SpringBoot 项目 引入jwt

<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>work.superstr</groupId>
  <artifactId>Superstr</artifactId>
   <version>0.0.1-SNAPSHOT</version><!-- SNAPSHOT:快照版本,仍处开发中的版本(不稳定),RELEASE:正式版本,对外发布的稳定版本 -->
  <packaging>war</packaging><!-- jar:普通项目,pom:父级项目,war:服务项目 -->
  <!-- 继承spring-boot-starter-parent --> 
  <parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.0.0.RELEASE</version>
  </parent>
  <!-- 添加Maven依赖 -->
  <dependencies>
	<dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.5.0</version>
    </dependency>
    <dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-web</artifactId>
	    <!-- 移除嵌入式tomcat插件 -->
	    <exclusions>
	        <exclusion>
	            <groupId>org.springframework.boot</groupId>
	            <artifactId>spring-boot-starter-tomcat</artifactId>
	        </exclusion>
	    </exclusions>
	</dependency>
    <!-- SpringBoot内嵌tomcat(spring-boot-starter-web已经默认添加了这个jar包的依赖) -->
    <!-- 
    <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-tomcat</artifactId>
	</dependency>
	-->
	<dependency> 
		<groupId>org.springframework.boot</groupId> 
		<artifactId>spring-boot-starter-web</artifactId> 
	</dependency>
	<!--SpringBoot工具,改善开发时体验的模块:classpath里对文件任何操作都会触发应用程序重新启动 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
 
	<dependency>
         <groupId>com.alibaba</groupId>
         <artifactId>fastjson</artifactId>
         <version>1.2.15</version>
    </dependency>
    <dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
</dependency>
    <dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
  </dependencies>
  <build>
  	<plugins>
  		<!-- SpringBoot Maven插件,可以打包成可执行的jar或war -->
  		<plugin>
  			<groupId>org.springframework.boot</groupId> 
			<artifactId>spring-boot-maven-plugin</artifactId>
			<!-- maven install 没有指定<mainClass>或者继承了spring-boot-starter-parent并且<start-class>属性未配置时,会自动寻找签名是public static void main(String[] args)的方法... 所以插件懵逼了,两个妹子和谁在一起呢... -->
			<configuration>
        		<mainClass>work.superstr.SuperstrApplication</mainClass>
		    </configuration>
		    <!-- 所述spring-boot-starter-parentPOM包括<executions>配置以结合repackage目标。如果您不使用父POM,则需要自己声明此配置 -->
  			<executions>
		        <execution>
		            <goals>
		                <goal>repackage</goal>
		            </goals>
		        </execution>
		    </executions>
  		</plugin>
  	</plugins>
  </build>
</project>

JWT工具类

package work.superstr.jwt;

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

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;

public class JwtUtils {
	public static void main(String[] args) {
		Map<String, String> map = new HashMap<String, String>();
		String generateToken = generateToken(map);
		System.out.println(generateToken);
		Map<String, Claim> validateToken = validateToken(generateToken);
		for(Entry<String, Claim> entry : validateToken.entrySet()) {
			System.out.println(entry.getKey()+"==========="+entry.getValue());
		}
	}
	
	public static final String secrect="[email protected]";
    public static final long EXPIRE_DATE=259200000;//1000*60*60*24*3
    private static Algorithm algorithm = Algorithm.HMAC256(secrect);

    /**
     * 生成Token 
     * @param other
     * @return
     */
    public static String generateToken(Map<String,String> claim){
    	JWTCreator.Builder builder=JWT.create();
    	/*
    	JWT结构:
     		base64UrlEncode(header)
     		.base64UrlEncode(payload)
     		.HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
        Header通常由两部分组成:令牌的类型和常用的散列算法
        */
        Map<String, Object> header = new HashMap<String, Object>();
        header.put("typ", "JWT");
        header.put("alg", "HS256");
        builder.withHeader(header);
        /*
        Payload(载荷),标准中注册的声明:
	        iss (issuer):JWT签发者
	        sub (subject):JWT所面向的用户
	        aud (audience):接受JWT的一方
			exp (expiration time):过期时间
			nbf (Not Before):生效时间
			iat (Issued At):签发时间
			jti (JWT ID):JWT的唯一标识
        */
        Date date = new Date();
        builder.withIssuer("superstr") //签发人
        .withIssuedAt(date)//签发时间
        .withExpiresAt(new Date(date.getTime()+EXPIRE_DATE));//设置有效期
        for(Entry<String,String> entry:claim.entrySet()){
        	builder.withClaim(entry.getKey(),entry.getValue());
        }
        /*
        Signature 签名
         */
        String token = builder.sign(algorithm);
        return token;
    }
 
    /**
     * 验证Token
     * @param token
     * @return
     */
    public static Map<String,Claim> validateToken(String token){
        try {
            JWTVerifier verifier = JWT.require(algorithm)
            .withIssuer("superstr")
            .build();
            DecodedJWT jwt = verifier.verify(token);
            return jwt.getClaims();
        } catch (JWTVerificationException e){
            e.printStackTrace();
            return null;
        }
    }
}

Result工具类

package work.superstr;

public class Result<T> {
	private int code;
	private String message;
	private T data;
	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;
	}
}

用户对象

package work.superstr;

import java.io.Serializable;

public class User implements Serializable{
	private static final long serialVersionUID = 1L;
	private String username;
	private String password;
	public User() {}
	public User(String username, String password) {
		this.username = username;
		this.password = password;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}

Controller 实例 login登入成功时,将token返回给客户端

package work.superstr;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import work.superstr.jwt.JwtUtils;

@RestController
public class LoginController
{
	
	/*@Autowired
	private StringRedisTemplate stringRedisTemplate;*/
	
    @RequestMapping("/login")
    public Result<User> login(HttpServletRequest request,HttpServletResponse response, HttpSession httpSession)
    {
    	String username = request.getParameter("username");
		String password = request.getParameter("password");
		User user = new User(username, password);
		Result<User> result = new Result<User>();
		if("root".equals(user.getUsername())&&"123456".equals(user.getPassword())) {
			Map<String, String> claim = new HashMap<>();
            claim.put("userName", user.getUsername());
            String generateToken = JwtUtils.generateToken(claim);
            result.setCode(0);
            result.setMessage("登入成功 返回Token:"+generateToken);
            result.setData(user);
		} else {
			result.setCode(-1);
			result.setMessage("密码或账号错误");
		}
		return result;
    	/*String username = request.getParameter("username");
		String password = request.getParameter("password");
		User user = new User();
		user.setUsername(username);
		user.setPassword(password);
		
		if("root".equals(user.getUsername())&&"123456".equals(user.getPassword())) {
			httpSession.setAttribute("user",user);
			Cookie JSESSIONID = new Cookie("JESSIONID",httpSession.getId());
			JSESSIONID.setPath("/");
			response.addCookie(JSESSIONID);
			stringRedisTemplate.opsForValue().set("user_"+user.getUsername(), SerializeUtil.serialize(user).toString());
		}
		Map<String,String> map = new HashMap<String,String>();
		map.put("mssage", "登入成功");
		 Cookie cname = new Cookie("name", user.getUsername());
		    Cookie cpwd = new Cookie("pwd", user.getPassword());
			//设置cookie过期时间
		    cname.setMaxAge(60*60);
			cpwd.setMaxAge(60*60);
			//设置全路径
		    cname.setPath("/");
		    cpwd.setPath("/");
		    //响应请求 存入cookie
			response.addCookie(cname);
			response.addCookie(cpwd);
		
		Cookie c = new Cookie("user", SerializeUtil.serialize(user).toString());
		response.addCookie(c);
		return map;*/
    }
    
    @RequestMapping("/logout")
    public void logout(HttpServletRequest request,HttpServletResponse response, HttpSession httpSession) throws IOException
    {
    	httpSession.removeAttribute("user");
    	response.sendRedirect(request.getContextPath()+"/login.html");
    }
    
    @RequestMapping("/getData")
    public Map<String,Object> getData() {
    	Map<String,Object> data = new HashMap<String,Object>();
    	data.put("user1", new User("username1","password1"));
    	data.put("user1", new User("username2","password2"));
    	return data;
    }
}

添加过滤器,排除 login login.html的过滤,验证是否已经登入,没有登入跳转登入页login.html

package work.superstr.jwt;

import java.io.IOException;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.stereotype.Component;

import com.auth0.jwt.interfaces.Claim;

@Component
@ServletComponentScan
@WebFilter
public class JwtFilter implements Filter {
    String[] includeUrls = new String[]{"/login","/login.html","/register","/register.html"};
    /**
     * 是否需要过滤
     */
    public boolean isNeedFilter(String uri) {
        for (String includeUrl : includeUrls) {
            if(includeUrl.equals(uri)) {
                return false;
            }
        }
        return true;
    }
	
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    	 HttpServletRequest request = (HttpServletRequest) servletRequest;
         HttpServletResponse response = (HttpServletResponse) servletResponse;
         String uri = request.getRequestURI();
         boolean needFilter = isNeedFilter(uri);
         if (!needFilter) { //不需要过滤直接传给下一个过滤器
             filterChain.doFilter(servletRequest, servletResponse);
         } else {//需要过滤器
        	  boolean isLog = false;//是否登入
        	  String token = request.getParameter("token");
        	  Map<String, Claim> validateToken = JwtUtils.validateToken(token);
        	  if(validateToken!=null) {
        		  isLog = true;
        	  }
        	  if(isLog) {
                  filterChain.doFilter(request, response);
	          }else{
                  String requestType = request.getHeader("X-Requested-With");
                  //判断是否是ajax请求
                  if(requestType!=null && "XMLHttpRequest".equals(requestType)){
                      response.getWriter().write("没有登入");
                  }else{
                      response.sendRedirect(request.getContextPath()+"/login.html");
                  }
                  return;
              }
	     }
    }
    

    @Override
    public void destroy() {

    }
}

浏览器请求http://192.168.0.144:8090/getData 发现跳转login.html

浏览器请求http://192.168.0.144:8090/login?username=root&password=123456 进行登入

JWT 单点登入

token 可以存在Cookie(会随每次HTTP请求一起被传递到服务器端,排除js、css
、iamge等静态文件) Web缓存中 

请求http://192.168.0.144:8090/getData?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBlcnN0ciIsImV4cCI6MTU1NTU4NTkzNywidXNlck5hbWUiOiJyb290IiwiaWF0IjoxNTU1MzI2NzM3fQ.hnS31Cy4c6lSJKu2rmDj65CpQ6EGpJ9KnvcT_wUEYbk

已经成功访问到数据

JWT 单点登入