简介:
在企业发展初期,企业使⽤的系统很少,通常⼀个或者两个,每个系统都有⾃⼰的登录模块,运营⼈员每天⽤⾃⼰的账号登录,很⽅便。 但随着企业的发展,⽤到的系统随之增多,运营⼈员在操作不同的系统时,需要多次登录,⽽且每个系统的账号都不⼀样,这对于运营⼈员来说,很不⽅便。于是,就想到是不是可以在⼀个系统登录,其他系统就不⽤登录了呢?这就是单点登录要解决的问题。 单点登录英⽂全称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 星期六 到这里我们的代码就完了,是不是很简短呢。。。。。。 有问题欢迎在评论区评论,感谢你的阅读。。。。