在做微信授權(quán)登陸的時候,由于使用shiro框架進行認證登陸,沒有認證授權(quán)無法建立會話。
數(shù)據(jù)庫中的密碼經(jīng)過md5加密员魏,通過openid查詢出賬號密碼也無法進行授權(quán)登陸,所以需要實現(xiàn)Shiro免密授權(quán)登陸功能叠聋,以防后續(xù)踩坑逆趋。
1.整體思路
自定義token,加入免密標識符晒奕,在進行授權(quán)時判斷是密碼登陸或無密碼登陸闻书,自定義密碼認證方法,即寫一個方法繼承HashedCredentialsMatcher脑慧,重寫其中的doCredentialsMatch魄眉,將其中的token改寫為自定義的token,最后將自己寫的密碼認證方法注入shiro
2.源碼分析
在shiro的配置文件中有個配置credentialsMatcher闷袒,該方法為重寫方法坑律,自定義登陸模式,可設(shè)置密碼錯誤鎖定功能囊骤,我們需要的就是改寫這個方法
1)在ShiroDbRealm中晃择,認證使用的是UserPasswordToken
其默認使用username,password進行認證
網(wǎng)上有方法另寫token繼承UsernamePasswordToken重寫getCredentials方法修改getPassword() 為 return null。此方法過于簡單粗暴也物,順帶連密碼認證也給刪除了宫屠,不建議使用。
2)Shiro的密碼認證在org.apache.shiro.authc.credential包下HashedCredentialsMatcher.class方法內(nèi)
其中密碼認證:doCredentialsMatch方法
3.免密登陸方法
1)創(chuàng)建一個枚舉類(enum )LoginType
public enum LoginType {
PASSWORD("password"), // 密碼登錄
NOPASSWD("nopassword"); // 免密登錄
private String code;// 狀態(tài)值
private LoginType(String code) {
this.code = code;
}
public String getCode () {
return code;
}
}
2)自定義token 繼承UsernamePasswordToken,通過構(gòu)造方法來區(qū)分 賬號密碼登陸(password) 和 免密登陸(nopassword)
package com.pasic.commons.shiro;
import org.apache.shiro.authc.UsernamePasswordToken;
public class EasyTypeToken extends UsernamePasswordToken {
private static final long serialVersionUID = -2564928913725078138L;
private LoginType type;
public EasyTypeToken() {
super();
}
public EasyTypeToken(String username, String password, LoginType type, boolean rememberMe, String host) {
super(username, password, rememberMe, host);
this.type = type;
}
/**免密登錄*/
public EasyTypeToken(String username) {
super(username, "", false, null);
this.type = LoginType.NOPASSWD;
}
/**賬號密碼登錄*/
public EasyTypeToken(String username, String password) {
super(username, password, false, null);
this.type = LoginType.PASSWORD;
}
public LoginType getType() {
return type;
}
public void setType(LoginType type) {
this.type = type;
}
}
3)修改ShiroDbRealm:將UsernamePasswordToen修改為自定義token
4)找到RetryLimitCredentialsMatcher重寫doCredentialsMatch,將token強轉(zhuǎn)為自定義token滑蚯,如果是免密登陸則直接返回 true
@Override
public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
EasyTypeToken tk = (EasyTypeToken) authcToken;
if(tk.getType().equals(LoginType.NOPASSWD)){
return true;
}
String username = (String) authcToken.getPrincipal();
//retry count + 1
AtomicInteger retryCount = passwordRetryCache.get(username);
if(retryCount == null) {
retryCount = new AtomicInteger(0);
passwordRetryCache.put(username, retryCount);
}
if(retryCount.incrementAndGet() > 5) {
//if retry count > 5 throw
logger.warn("username: " + username + " tried to login more than 5 times in period");
throw new ExcessiveAttemptsException("用戶名: " + username + " 密碼連續(xù)輸入錯誤超過5次达址,鎖定半小時话原!");
} else {
passwordRetryCache.put(username, retryCount);
}
boolean matches = super.doCredentialsMatch(authcToken, info);
if(matches) {
//clear retry data
passwordRetryCache.remove(username);
}
return matches;
}
就此配置完成
Login調(diào)用
1)賬號密碼登陸
Subject user = SecurityUtils.getSubject();
EasyTypeToken token = new EasyTypeToken(username, password);
user.login(token);
2)免密登陸
Subject user = SecurityUtils.getSubject();
EasyTypeToken token = new EasyTypeToken(username);
user.login(token);
通過不同的構(gòu)造函數(shù)生成不同的LoginType光稼,從而實現(xiàn)賬號密碼登陸與免密登陸分離步鉴,完成。