在實際開發中,一般我們使用shiro進行用戶密碼登錄,有時候面對一些常見的需求,不得不再已有的框架上進行修改,比如說,增加一種使用手機驗證碼登錄的登錄方式。這時候,我們可以根據已有的框架,對其稍微修改一下便可滿足需求。
public class ShiroDBRealm extends AuthorizingRealm{
private static final String className = "ShiroDBRealm";
@Autowired
private SecurityService securityService;
public ShiroDBRealm(){
setAuthenticationTokenClass(UsernamePasswordLoginTypeToken.class);
}
@Override
public String getName() {
return "shiroDBRealm";
}
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authenticationToken) throws AuthenticationException {
try {
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken ;
User user = securityService.login(token.getUsername(),new String(token.getPassword()));
String pwd = new String(token.getPassword());
}
if(user!=null){
return new SimpleAuthenticationInfo(user, pwd,getName()) ;
}else{
return null ;
}
} catch (Exception e) {
e.printStackTrace() ;
}
return null ;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
final String meth = "doGetAuthorizationInfo";
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo() ;
User user = (User)principal.getPrimaryPrincipal();
List<Authority> authorityList = securityService.getAuthorityList(user);
for(Authority authority:authorityList){
info.addStringPermission(authority.getAuthorityString());
}
return info;
}
}
一般來說,我們是像上面那樣實現AuthorizingRealm接口,並且實現doGetAuthenticationInfo和doGetAuthorizationInfo兩個方法,分別進行身份驗證和授權。而如果要實現免密碼登錄,也就是說要提供一個不需要通過密碼就能夠實現身份驗證的service方法,所以在這裏,我們可以寫一個根據用戶賬號來獲取用戶信息的方法。同時需要對token增加屬性,而且還要處理返回的SimpleAuthenticationInfo如何免密碼通過的問題。也就是說,在認證和授權這裏,我們需要做的工作是有:
1.提供使用用戶名獲取用戶信息的service方法。
2.對錶單提交產生的token增加必要的屬性。
3.處理返回的SimpleAuthenticationInfo的問題。
其中第1點是根據數據庫的設計來實現的,第2點直接繼承UsernamePasswordToken 類,增加需要用到的屬性,第3點根據shiro的實現,返回的SimpleAuthenticationInfo必須有密碼才能通過,所以可以給它一個通用的密碼。
public class UsernamePasswordLoginTypeToken extends UsernamePasswordToken{
private String loginType = "0";// 0爲用戶密碼登錄,1爲手機驗證碼登錄
private String utoken;
private String uphoneNum;
public String getUphoneNum() {
return uphoneNum;
}
public void setUphoneNum(String uphoneNum) {
this.uphoneNum = uphoneNum;
}
public String getLoginType() {
return loginType;
}
public void setLoginType(String loginType) {
this.loginType = loginType;
}
public String getUtoken() {
return utoken;
}
public void setUtoken(String utoken) {
this.utoken = utoken;
}
public UsernamePasswordLoginTypeToken(){
super();
}
public UsernamePasswordLoginTypeToken(final String username, final String password,
final boolean rememberMe, final String host,String loginType,String utoken,String uphoneNum) {
super(username, password, rememberMe, host);
this.loginType = loginType;
this.utoken = utoken;
this.uphoneNum = uphoneNum;
}
}
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authenticationToken) throws AuthenticationException {
try {
UsernamePasswordLoginTypeToken token = (UsernamePasswordLoginTypeToken)authenticationToken ;
User user = null;
String pwd = null;
String loginType = token.getLoginType();
if(loginType.equals(LoginType.PHONE_CAPTCHA.getType())){
String pthoneNum = token.getUphoneNum();
String key = pthoneNum + "-token";
String utoken = RedisAPI.get(key);
if(!utoken.equals(token.getUtoken())){
throw new UtokenException("utoken不正確");
}else{
RedisAPI.del(key);
user = securityService.loginWithoutPwd(token.getUsername());
pwd = "123";
}
}else{
user = securityService.login(token.getUsername(),new String(token.getPassword()));
pwd = new String(token.getPassword());
}
if(user!=null){
return new SimpleAuthenticationInfo(user, pwd,getName()) ;
}else{
return null ;
}
} catch (Exception e) {
e.printStackTrace() ;
}
return null ;
}
我們一般使用spring和shiro結合使用,而登錄方式使用表單提交的方式,所以使用FormAuthenticationFilter類進行過濾。表單提交過來的數據通過FormAuthenticationFilter產生UsernamePasswordToken,如果我們需要對token增加參數那麼肯定要增加屬性,而增加屬性的方法就是繼承UsernamePasswordToken,所以在這裏我們需要重寫FormAuthenticationFilter的createToken方法。
protected AuthenticationToken createToken(String username, String password,
ServletRequest request, ServletResponse response) {
boolean rememberMe = isRememberMe(request);
String host = getHost(request);
String loginType = LoginType.USER_PASSWORD.getType();
if(request.getParameter("loginType")!=null && !"".equals(request.getParameter("loginType").trim())){
loginType = request.getParameter("loginType");
}
return new UsernamePasswordLoginTypeToken(username, password,rememberMe,host,loginType,
request.getParameter("utoken"),request.getParameter("uphoneNum"));
}