shiro免密码登录

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

你可能感兴趣的:(shiro)