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 进行登入
token 可以存在Cookie(会随每次HTTP请求一起被传递到服务器端,排除js、css
、iamge等静态文件) Web缓存中
已经成功访问到数据