使用Jwt实现简单登陆效果

简介:

​ 在企业发展初期,企业使⽤的系统很少,通常⼀个或者两个,每个系统都有⾃⼰的登录模块,运营⼈员每天⽤⾃⼰的账号登录,很⽅便。
​ 但随着企业的发展,⽤到的系统随之增多,运营⼈员在操作不同的系统时,需要多次登录,⽽且每个系统的账号都不⼀样,这对于运营⼈员来说,很不⽅便。于是,就想到是不是可以在⼀个系统登录,其他系统就不⽤登录了呢?这就是单点登录要解决的问题。
​ 单点登录英⽂全称Single Sign On,简称就是SSO。它的解释是:在多个应⽤系统中,只需要登录⼀次,就可以访问其他相互信任的应⽤系统。

流程:


如图所示,图中有四个系统,分别是SSO系统,Application1,Application2,Application3。在Application1、Application2、Application3这三个系统中没有登陆模块,只有SSO中才有登陆模块,当你要登陆的时候,会跳转到SSO模块,登陆成功后会跳转到对应的功能模块中。

技术实现:

第⼀步:引⼊我们需要的依赖:

<!-- jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.1</version>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>

<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

第⼆步:我们需要⼀个User类:

User

public class User {

private String username;

private String 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;
}
}

第三步:我们需要认证的基本类:

JWTToken

 @Data
public class JWTToken implements AuthenticationToken {

private String token;

private String expireAt;

@Override
public Object getPrincipal() {

return token;

}

@Override
public Object getCredentials() {

return token;

}

public JWTToken(String token) {
this.token = token;
}

public JWTToken(String token, String expireAt) {
this.token = token;
this.expireAt = expireAt;
}

}

JWTUtil

/**
* @author Lenovo
*/
public class JWTUtil {

private static final Logger LOGGER = LoggerFactory.getLogger(JWTUtil.class);

private static final Integer EXPIRE_TIME = SpringContextUtil.getBean(E17Properties.class).getJwtTimeOut() * 1000;

/**
* 校验token是否正确
*
* @param token
* @param username
* @param password
* @return
*/
public static boolean verify(String token, String username, String password) {

try {

Algorithm algorithm = Algorithm.HMAC256(password);

JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();

LOGGER.info("token is valid");

return true;

} catch (Exception e) {

LOGGER.error("token is invalid{}", e.getMessage());

return false;
}
}

/**
* 根据token获取用户名
*
* @param token
* @return
*/
public static String getUserName(String token) {

try {

DecodedJWT decode = JWT.decode(token);

return decode.getClaim("username").asString();

} catch (JWTDecodeException e) {

LOGGER.error("error:{}", e.getMessage());

return null;
}
}

/**
* 生成token
*
* @param username
* @param password
* @return
*/
public static String sign(String username, String password) {

try {

username = StringUtils.lowerCase(username);

Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);

Algorithm algorithm = Algorithm.HMAC256(password);

return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);

} catch (Exception e) {

LOGGER.error("error:{}", e.getMessage());

return null;
}
}
}

第四步:⾃定义的Properties:(主要⽤户设置⼀些基本的参数)

​ 先上配置文件:

E17Properties.properties

#自定义
e17.auth.anonUrl= /login

e17.auth.jwtTimeOut=3600

E17Properties

/**
* @author Lenovo
*/
@SpringBootConfiguration
@ConfigurationProperties(prefix = "e17.auth")
@PropertySource(value = "classpath:E17Properties.properties")
public class E17Properties {

/**
* 免认证URl
*/
private String anonUrl;

/**
* token有效期时间为1天
*/
private Integer jwtTimeOut = 86400;

public String getAnonUrl() {
return anonUrl;
}

public void setAnonUrl(String anonUrl) {
this.anonUrl = anonUrl;
}

public Integer getJwtTimeOut() {
return jwtTimeOut;
}

public void setJwtTimeOut(Integer jwtTimeOut) {
this.jwtTimeOut = jwtTimeOut;
}
}

第五步:工具类:

MD5Util:

/**
* @author Lenovo
*/
public class MD5Util {

protected MD5Util(){

}

private static final String ALGORITH_NAME = "md5";

private static final int HASH_ITERATIONS = 2;

public static String encrypt(String password) {
return new SimpleHash(ALGORITH_NAME, password, ByteSource.Util.bytes(password), HASH_ITERATIONS).toHex();
}

public static String encrypt(String username, String password) {
return new SimpleHash(ALGORITH_NAME, password, ByteSource.Util.bytes(username.toLowerCase() + password),
HASH_ITERATIONS).toHex();
}

}

SpringContextUtil

/**
* @author Lenovo
*/
@Component
public class SpringContextUtil implements ApplicationContextAware {

private static ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

SpringContextUtil.applicationContext = applicationContext;
}

public static Object getBean(String name) {
return applicationContext.getBean(name);
}
public static <T> T getBean(Class<T> clazz){
return applicationContext.getBean(clazz);
}

public static <T> T getBean(String name, Class<T> requiredType) {
return applicationContext.getBean(name, requiredType);
}

public static boolean containsBean(String name) {
return applicationContext.containsBean(name);
}

public static boolean isSingleton(String name) {
return applicationContext.isSingleton(name);
}

public static Class<?> getType(String name) {
return applicationContext.getType(name);
}
}

ApiResponse

/**
* @author Lenovo
*/
public class ApiResponse extends HashMap<String, Object> {


public ApiResponse message(String msg) {

this.put("msg", msg);

return this;
}

public ApiResponse data(Object data) {

this.put("data", data);

return this;
}

@Override
public ApiResponse put(String key, Object value) {

super.put(key, value);

return this;
}
}

CookieUtils

package com.e17.common.util;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

/**
* Cookie工具类
* @author Lenovo
*/
public class CookieUtils {

static final Logger logger = LoggerFactory.getLogger(CookieUtils.class);

/**
* 得到Cookie的值, 不编码
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName) {
return getCookieValue(request, cookieName, false);
}

/**
* 得到Cookie的值,
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null){
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
if (isDecoder) {
retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
} else {
retValue = cookieList[i].getValue();
}
break;
}
}
} catch (UnsupportedEncodingException e) {
logger.error("Cookie Decode Error.", e);
}
return retValue;
}

/**
* 得到Cookie的值,
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null){
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
break;
}
}
} catch (UnsupportedEncodingException e) {
logger.error("Cookie Decode Error.", e);
}
return retValue;
}

/**
* 生成cookie,并指定编码
* @param request 请求
* @param response 响应
* @param cookieName name
* @param cookieValue value
* @param encodeString 编码
*/
public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, String encodeString) {
setCookie(request,response,cookieName,cookieValue,null,encodeString, null);
}

/**
* 生成cookie,并指定生存时间
* @param request 请求
* @param response 响应
* @param cookieName name
* @param cookieValue value
* @param cookieMaxAge 生存时间
*/
public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge) {
setCookie(request,response,cookieName,cookieValue,cookieMaxAge,null, null);
}

/**
* 设置cookie,不指定httpOnly属性
*/
public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge, String encodeString) {
setCookie(request,response,cookieName,cookieValue,cookieMaxAge,encodeString, null);
}

/**
* 设置Cookie的值,并使其在指定时间内生效
*
* @param cookieMaxAge
* cookie生效的最大秒数
*/
public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge, String encodeString, Boolean httpOnly) {
try {
if(StringUtils.isBlank(encodeString)) {
encodeString = "utf-8";
}

if (cookieValue == null) {
cookieValue = "";
} else {
cookieValue = URLEncoder.encode(cookieValue, encodeString);
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxAge != null && cookieMaxAge > 0)
cookie.setMaxAge(cookieMaxAge);
if (null != request)// 设置域名的cookie
cookie.setDomain(getDomainName(request));
cookie.setPath("/");

if(httpOnly != null) {
cookie.setHttpOnly(httpOnly);
}
response.addCookie(cookie);
} catch (Exception e) {
logger.error("Cookie Encode Error.", e);
}
}

/**
* 得到cookie的域名
*/
private static final String getDomainName(HttpServletRequest request) {
String domainName = null;

String serverName = request.getRequestURL().toString();
if (serverName == null || serverName.equals("")) {
domainName = "";
} else {
serverName = serverName.toLowerCase();
serverName = serverName.substring(7);
final int end = serverName.indexOf("/");
serverName = serverName.substring(0, end);
final String[] domains = serverName.split("\\.");
int len = domains.length;
if (len > 3) {
// www.xxx.com.cn
domainName = domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
} else if (len <= 3 && len > 1) {
// xxx.com or xxx.cn
domainName = domains[len - 2] + "." + domains[len - 1];
} else {
domainName = serverName;
}
}

if (domainName != null && domainName.indexOf(":") > 0) {
String[] ary = domainName.split("\\:");
domainName = ary[0];
}
return domainName;
}
}

实际操作:

定义AuthController:

AuthController


/**
* 授权Controller
*
* @author Lenovo
*/
@Controller
@Validated
public class AuthController {

@Autowired(required = false)
private AuthService authService;

private static final Logger LOGGER = LoggerFactory.getLogger(AuthController.class);

/**
* 登录授权
*
* @param username
* @param password
* @return
*/
@PostMapping("accredit")
public ResponseEntity<ApiResponse> accredit(@RequestParam(value = "username", required = true) String username,
@RequestParam(value = "password", required = true) String password,
HttpServletRequest request, HttpServletResponse response) {

ApiResponse apiResponse = this.authService.accredit(username, password, request, response);

return ResponseEntity.ok(apiResponse);
}

/**
* 登录校验接口
*
* @param token
* @return
*/
@PostMapping("verify")
public ResponseEntity<ApiResponse> verify(@RequestParam("token") String token) {

String userName = JWTUtil.getUserName(token);

return ResponseEntity.ok(new ApiResponse().message(userName));
}
}

定义AuthService

AuthService


/**
* @author Lenovo
*/
public interface AuthService {

// 登录授权
ApiResponse accredit(String username, String password, HttpServletRequest request, HttpServletResponse response);
}

定义AuthServiceImpl

AuthServiceImpl


/**
* 授权Service
*
* @author Lenovo
*/
@Service
public class AuthServiceImpl implements AuthService {

@Autowired(required = false)
private E17Properties e17Properties;

/**
* 登录授权
*
* @param username
* @param password
* @return
*/
@Override
public ApiResponse accredit(String username, String password, HttpServletRequest request, HttpServletResponse response) {

String token = null;

try {
// 存在
// 生成token
token = JWTUtil.sign(username, password);

// 将其保存到Cookie中
CookieUtils.setCookie(request, response, "TOKEN", token, e17Properties.getJwtTimeOut());
// 返回token
return new ApiResponse().data(token).message("登录成功");

} catch (Exception e) {

e.printStackTrace();

return new ApiResponse().message("服务器错误").data(null);
}

}
}

2020-03-14 20:54:55 星期六
到这里我们的代码就完了,是不是很简短呢。。。。。。
有问题欢迎在评论区评论,感谢你的阅读。。。。