提示:若有疑問請私信聯繫、下方有源代碼地址,請自行拿取java
網上SSO的框架不少,此篇文章使用的是自寫的SSO來實現簡單的登陸受權功能,目的在於擴展性,權限這方面,自寫擴展性會好點。git
提示:如下是本篇文章正文內容,下面案例可供參考web
代碼以下(示例):redis
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
<relativePath/>
</parent>
<dependencies>
<dependencies>
<dependency>
<artifactId>hyh-boot-starter-redis</artifactId>
<groupId>com.hyh.redis</groupId>
<version>1.0.0</version>
</dependency>
</dependencies>
複製代碼
ILogin接口:spring
package com.hyh.sso;
import com.hyh.sso.po.LoginResult;
/** * 登陸接口 * * @Author: heyuhua * @Date: 2021/1/8 17:14 */
public interface ILogin {
/** * 登陸 * * @param account 用戶名 * @param password 密碼 * @param callbackUrl 用戶驗證回調URL * @return */
LoginResult login(String account, String password, String callbackUrl);
}
複製代碼
登陸狀態枚舉:json
package com.hyh.sso;
/** * 登陸狀態枚舉 * * @Author: heyuhua * @Date: 2021/1/8 16:59 */
public enum LoginStatus {
SUCCESS(1, "登陸成功"), ING(0, "登陸中"), FAIL(-1, "登陸失敗"),
ERROR(-2, "登陸異常"), CALLBACK_ERROR(-3, "登陸回調異常"), ACCOUNT_LOCK(-4, "帳戶被鎖定"),
EXPIRE(-5,"登陸用戶已過時");
/** * 登陸狀態碼 */
private int code;
/** * 登陸狀態消息 */
private String message;
private LoginStatus(int code, String message) {
this.code = code;
this.message = message;
}
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;
}
}
複製代碼
登陸類型枚舉:服務器
package com.hyh.sso;
/** * 登陸類型 * * @Author: heyuhua * @Date: 2021/1/8 17:16 */
public enum LoginTypes {
/** * 登入 */
IN,
/** * 登出 */
OUT;
}
複製代碼
登陸常規接口:markdown
package com.hyh.sso;
package com.hyh.sso.service;
import com.hyh.sso.ILogin;
/** * 常規登陸接口 * * @Author: heyuhua * @Date: 2021/1/8 17:54 */
public interface LoginService extends ILogin {
}
複製代碼
登陸接口實現:app
package com.hyh.sso.service.impl;
import com.alibaba.fastjson.JSON;
import com.hyh.sso.LoginStatus;
import com.hyh.sso.po.LoginResult;
import com.hyh.sso.po.LoginUser;
import com.hyh.sso.service.LoginService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/** * 登陸接口實現 * * @Author: heyuhua * @Date: 2021/1/8 17:56 */
@Service
public class LoginServiceImpl implements LoginService {
private static final Logger LOG = LoggerFactory.getLogger(LoginServiceImpl.class);
/** * rest接口請求模板 */
private static RestTemplate restTemplate = new RestTemplate();
@Override public LoginResult login(String account, String password, String callbackUrl) {
LoginResult loginResult = null;
try {
HttpHeaders headers = new HttpHeaders();
//設置請求媒體數據類型
headers.setContentType(MediaType.APPLICATION_JSON);
//設置返回媒體數據類型
headers.add("Accept", MediaType.APPLICATION_JSON.toString());
HttpEntity<String> formEntity = new HttpEntity<String>(JSON.toJSONString(new LoginUser(account, password)), headers);
loginResult = restTemplate.postForObject(callbackUrl, formEntity, LoginResult.class);
} catch (Exception e) {
LOG.error("login valid callback error", e);
return new LoginResult(LoginStatus.CALLBACK_ERROR);
}
return loginResult == null ? new LoginResult(LoginStatus.ERROR) : loginResult;
}
}
複製代碼
登陸用戶對象:框架
package com.hyh.sso.po;
/** * 登陸用戶對象 * * @Author: heyuhua * @Date: 2021/1/8 16:58 */
public class LoginUser {
/** * 帳號 */
private String account;
/** * 密碼 */
private String password;
/** * 登陸時間 */
private String loginTime;
public LoginUser(String account, String password) {
this.account = account;
this.password = password;
}
public LoginUser() {
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getLoginTime() {
return loginTime;
}
public void setLoginTime(String loginTime) {
this.loginTime = loginTime;
}
}
複製代碼
用戶Token對象:
package com.hyh.sso.po;
import com.hyh.utils.code.MD5;
import com.hyh.utils.common.StringUtils;
import java.util.Calendar;
/** * 用戶Token對象 * * @Author: heyuhua * @Date: 2021/1/8 17:07 */
public class UserToken {
/** * token */
private String token;
/** * 過時時間 */
private String expireTime;
public UserToken(String token, String expireTime) {
this.token = token;
this.expireTime = expireTime;
}
public UserToken() {
}
public static UserToken getUserToken() {
Calendar nowTime = Calendar.getInstance();
nowTime.add(Calendar.MINUTE, 30);
return new UserToken(MD5.getMD5String(StringUtils.ranStr(32)), String.valueOf(nowTime.getTimeInMillis()));
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getExpireTime() {
return expireTime;
}
public void setExpireTime(String expireTime) {
this.expireTime = expireTime;
}
/** * 生成Token */
private String generateToken() {
return MD5.getMD5String(StringUtils.ranStr(32));
}
}
複製代碼
登陸結果對象:
package com.hyh.sso.po;
import com.hyh.sso.LoginStatus;
import com.hyh.sso.LoginTypes;
/** * 登陸結果對象 * @Author: heyuhua * @Date: 2021/1/8 16:58 */
public class LoginResult {
/** * 登陸用戶對象 */
private LoginUser loginUser;
/** * 登陸用戶令牌 */
private UserToken userToken;
/** * 登陸狀態 */
private LoginStatus loginStatus;
/** * 登陸類型 */
private LoginTypes loginTypes;
public LoginResult(){}
public LoginResult(LoginStatus loginStatus) {
this.loginStatus = loginStatus;
}
public LoginUser getLoginUser() {
return loginUser;
}
public void setLoginUser(LoginUser loginUser) {
this.loginUser = loginUser;
}
public UserToken getUserToken() {
return userToken;
}
public void setUserToken(UserToken userToken) {
this.userToken = userToken;
}
public LoginStatus getLoginStatus() {
return loginStatus;
}
public void setLoginStatus(LoginStatus loginStatus) {
this.loginStatus = loginStatus;
}
public LoginTypes getLoginTypes() {
return loginTypes;
}
public void setLoginTypes(LoginTypes loginTypes) {
this.loginTypes = loginTypes;
}
@Override public String toString() {
return "LoginResult{" +
"loginUser=" + loginUser +
", userToken=" + userToken +
", loginStatus=" + loginStatus +
", loginTypes=" + loginTypes +
'}';
}
}
複製代碼
登陸助手:
package com.hyh.sso.helper;
import com.alibaba.fastjson.JSON;
import com.hyh.redis.helper.RedisHelper;
import com.hyh.sso.LoginStatus;
import com.hyh.sso.po.LoginResult;
import com.hyh.sso.po.LoginUser;
import com.hyh.sso.po.UserToken;
import com.hyh.sso.service.LoginService;
import com.hyh.utils.common.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/** * 登陸助手 * * @Author: heyuhua * @Date: 2021/1/8 17:13 */
@Component
public class LoginHelper {
/** * 日誌 */
private static final Logger LOG = LoggerFactory.getLogger(LoginHelper.class);
/** * 登陸用戶信息KEY */
private final String LOGIN_USER_KEY = "login:user:";
/** * 登陸用戶TOKEN KEY */
private final String LOGIN_TOKEN_KEY = "login:token:";
/** * 登陸失敗統計 KEY */
private final String LOGIN_FAIL_COUNT_KEY = "login:fail:count";
/** * 登陸失敗最多容許次數 */
private final long MAX_FAIL_COUNT = 5;
/** * 登陸服務 */
@Resource
private LoginService loginService;
/** * redis助手 */
@Autowired
private RedisHelper redisHelper;
/** * 登陸 * * @param account 用戶名 * @param password 密碼 * @param callbackUrl 回調URL * @return */
public LoginResult login(String account, String password, String callbackUrl) {
Assert.notNull(account, "account is null ");
Assert.notNull(password, "password is null ");
Assert.notNull(callbackUrl, "callbackUrl is null ");
//判斷帳戶是否屢次登陸失敗被鎖定
String value = redisHelper.getStringValue(LOGIN_FAIL_COUNT_KEY + account);
if (StringUtils.isNotBlank(value)) {
Long loginFailCount = Long.parseLong(value);
if (loginFailCount.longValue() >= MAX_FAIL_COUNT) {
return new LoginResult(LoginStatus.ACCOUNT_LOCK);
}
}
//登陸操做
LoginResult loginResult = loginService.login(account, password, callbackUrl);
switch (loginResult.getLoginStatus()) {
case SUCCESS:
//登陸成功
loginSuccess(loginResult);
break;
case FAIL:
//登陸失敗
loginFail(loginResult);
break;
case ERROR:
loginError(loginResult);
//登陸異常
break;
default:
break;
}
return loginResult;
}
/** * 註銷 * * @param account * @param token */
public void logout(String account, String token) {
Assert.notNull(account, "account is null ");
Assert.notNull(token, "token is null ");
removeKey(account, token);
}
/** * 註銷 * * @param token */
public void logout(String token) {
Assert.notNull(token, "token is null ");
removeKey(token);
}
/** * 獲取登陸用戶 * * @param token * @return */
public LoginUser getLoginUser(String token) {
Assert.notNull(token, "token is null ");
String value = redisHelper.getStringValue(LOGIN_USER_KEY + token);
if (StringUtils.isNotBlank(value)) {
return JSON.parseObject(value, LoginUser.class);
}
return null;
}
/** * 移除 key * * @param account * @param token */
private void removeKey(String account, String token) {
redisHelper.del(LOGIN_FAIL_COUNT_KEY + account);
redisHelper.del(LOGIN_TOKEN_KEY + account);
redisHelper.del(LOGIN_USER_KEY + token);
}
/** * 移除 Key * * @param token */
private void removeKey(String token) {
redisHelper.del(LOGIN_USER_KEY + token);
//其他的key到達過時時間自動過時
}
/** * 登陸異常 * * @param loginResult */
private void loginError(LoginResult loginResult) {
LOG.error("user 【" + loginResult.getLoginUser().getAccount() + "】 login error");
}
/** * 登陸失敗操做 * * @param loginResult */
private void loginFail(LoginResult loginResult) {
String key = LOGIN_FAIL_COUNT_KEY + loginResult.getLoginUser();
redisHelper.increment(key, 30 * 60 * 1000);
}
/** * 登陸成功操做 * * @param loginResult */
private void loginSuccess(LoginResult loginResult) {
LoginUser loginUser = loginResult.getLoginUser();
loginUser.setLoginTime(String.valueOf(new Date().getTime()));
UserToken userToken = UserToken.getUserToken();
redisHelper.set(LOGIN_TOKEN_KEY + loginResult.getLoginUser().getAccount(), JSON.toJSONString(userToken), 30, TimeUnit.MINUTES);
redisHelper.set(LOGIN_USER_KEY + userToken.getToken(), JSON.toJSONString(loginUser), 30, TimeUnit.MINUTES);
redisHelper.del(LOGIN_FAIL_COUNT_KEY + loginResult.getLoginUser());
}
}
複製代碼
代碼以下(示例):
server:
port: 8088
spring:
#redis配置
redis:
host: 192.168.6.134
port: 30511
password:
複製代碼
測試代碼以下(示例):
@Autowired
private LoginHelper loginHelper;
@Test public void testLogin() {
//測試時先開啓HyhBootApplication
String account = "hyh";
String password = "hyh-pwd";
String cllbackUrl = "http://localhost:8088/hyh/login";//在com.hyh.core.web下可查看
LoginResult loginResult = loginHelper.login(account, password, cllbackUrl);
System.out.println("loginResult:" + loginResult.toString());
}
//控制層代碼
@RequestMapping(value = "login", method = RequestMethod.POST)
public LoginResult login(@RequestBody LoginUser loginUser) {
Assert.notNull(loginUser.getAccount(), "account is null");
Assert.notNull(loginUser.getPassword(), "password is null");
LoginResult loginResult = new LoginResult(LoginStatus.SUCCESS);
loginResult.setLoginUser(loginUser);
//模擬直接返回登陸成功
return loginResult;
}
複製代碼
是否是感受很簡單?更多用法請點擊下方查看源碼,關注我帶你揭祕更多高級用法
源碼地址:點此查看源碼.