在实际开发中,一般我们使用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 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"));
}