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<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"));
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章