shiro学习之路-加密模块

来源:互联网 发布:unity3d像素游戏制作 编辑:程序博客网 时间:2024/06/16 12:38

一.shiro加密模块的使用

1.shiro是主流的权限管理框架,提供了认证,授权,回话管理,密码加密等功能,使得开发者更加便捷

2.具体实现采用MD5加密,而且进行加盐处理

二.代码实现

1.在自定义的认证类中,放回的AuthenticationInfo添加加盐参数

return new SimpleAuthenticationInfo(user,user.getPassword(),credentialsSalt,getName());

import java.util.Set;import org.apache.shiro.authc.AccountException;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.SimpleAuthenticationInfo;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;import org.apache.shiro.util.ByteSource;import org.springframework.beans.factory.annotation.Autowired;import com.yuanjun.shiro.bean.Module;import com.yuanjun.shiro.bean.Role;import com.yuanjun.shiro.bean.User;import com.yuanjun.shiro.service.UserService;/** *  * 类名称: MyAuthRealm * 类描述: 自己实现Realm认证授权操作 * @author yuanjun * 创建时间:2017年11月26日下午3:56:12 */public class MyAuthRealm extends AuthorizingRealm{@Autowiredprivate UserService userService;/** * 登陆认证 */protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {//获取登陆信息UsernamePasswordToken token = (UsernamePasswordToken) (authcToken);//获取登录名String username = token.getUsername();//查询数据库中的信息User user = userService.findUserByUserName(username);if(user==null){throw new AccountException("账号不存在");}//使用账号作为盐值ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername());return new SimpleAuthenticationInfo(user,user.getPassword(),credentialsSalt,getName());}/** * 授权 */protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {User user = (User) principals.getPrimaryPrincipal();//查找用户对于的角色Set<Role> roles = user.getRoles();//获取权限List<String> permissions=new ArrayList<String>();if(roles.size()>0){for(Role role: roles){Set<Module> module = role.getModules();if(module.size()>0){for (Module module2 : module) {permissions.add(module2.getMname());}}}}SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();      info.addStringPermissions(permissions);//将权限放入shiro中.return info;}}

2.添加密码匹配器

采用的SpringBoot的方式配置,即java配置类注册方式实现,通过跟踪源码的方式getCredentialsMatcher()调用密码匹配器,注册自己的Hash散序管理器,SecurityManager回去调用,传入的数据库的password会与用户输入的密码加盐处理后对比,不正确会抛出异常信息。实现的方式如下,自定义密码匹对,注入到权限登录器

方式一 java代码实现

//配置自定义的权限登录器    @Bean(name="authRealm")    public AuthRealm authRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher,    EhCacheManager ehCacheManager) {        AuthRealm authRealm=new AuthRealm();        authRealm.setCredentialsMatcher(matcher);        authRealm.setCacheManager(ehCacheManager);        return authRealm;    }    /**     * 密码匹配凭证管理器     *      * @return     */    @Bean(name = "hashedCredentialsMatcher")    public HashedCredentialsMatcher hashedCredentialsMatcher() {        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();        hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 散列算法:这里使用MD5算法;        hashedCredentialsMatcher.setHashIterations(1024);// 散列的次数,比如散列两次,相当于                                                            // md5(md5(""));        return hashedCredentialsMatcher;    }

方式二.xml配置

<bean id="jdbcRealm" class="com.java.shiro.realms.ShiroRealm">        <property name="credentialsMatcher">            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">                <property name="hashAlgorithmName" value="MD5"></property> <!-- 加密算法的名称 -->                <property name="hashIterations" value="1024"></property> <!-- 配置加密的次数 -->            </bean>        </property>    </bean>

三.源码跟踪过程

1.控制器接受登陆请求,subject.login----自定义认证类的doGetAuthenticationInfo()

public String loginUser(String username,String password,HttpSession session,    HttpServletRequest requset,boolean rememberMe) {    //加密处理,字符串处理    System.out.println(rememberMe);    username = username.trim();    UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(username,password);        Subject subject = SecurityUtils.getSubject();        try {            subject.login(usernamePasswordToken);   //完成登录            User user=(User) subject.getPrincipal();            session.setAttribute("user", user);            return "index";        }        catch(Exception e) {        requset.setAttribute("err", e.getMessage());            return "login";//返回登录页面        }            }

2.doGetAuthenticationInfo()中SimpleAuthenticationInfo--调用父类AuthenticatingRealm getAuthenticationInfo方法

import java.util.Set;import org.apache.shiro.authc.AccountException;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.SimpleAuthenticationInfo;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;import org.apache.shiro.util.ByteSource;import org.springframework.beans.factory.annotation.Autowired;import com.yuanjun.shiro.bean.Module;import com.yuanjun.shiro.bean.Role;import com.yuanjun.shiro.bean.User;import com.yuanjun.shiro.service.UserService;/** *  * 类名称: MyAuthRealm * 类描述: 自己实现Realm认证授权操作 * @author yuanjun * 创建时间:2017年11月26日下午3:56:12 */public class MyAuthRealm extends AuthorizingRealm{@Autowiredprivate UserService userService;/** * 登陆认证 */protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {//获取登陆信息UsernamePasswordToken token = (UsernamePasswordToken) (authcToken);//获取登录名String username = token.getUsername();//查询数据库中的信息User user = userService.findUserByUserName(username);if(user==null){throw new AccountException("账号不存在");}//使用账号作为盐值ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername());return new SimpleAuthenticationInfo(user,user.getPassword(),credentialsSalt,getName());}/** * 授权 */protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {User user = (User) principals.getPrimaryPrincipal();//查找用户对于的角色Set<Role> roles = user.getRoles();//获取权限List<String> permissions=new ArrayList<String>();if(roles.size()>0){for(Role role: roles){Set<Module> module = role.getModules();if(module.size()>0){for (Module module2 : module) {permissions.add(module2.getMname());}}}}SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();      info.addStringPermissions(permissions);//将权限放入shiro中.return info;}}

3.进行认证与用户信息对比

 public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        AuthenticationInfo info = getCachedAuthenticationInfo(token);        if (info == null) {            //otherwise not cached, perform the lookup:            info = doGetAuthenticationInfo(token);            log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);            if (token != null && info != null) {                cacheAuthenticationInfoIfPossible(token, info);            }        } else {            log.debug("Using cached authentication info [{}] to perform credentials matching.", info);        }        if (info != null) {            assertCredentialsMatch(token, info);        } else {            log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);        }        return info;    }

4.assertCredentialsMatch 调用自定义密码匹配器

 protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {        CredentialsMatcher cm = getCredentialsMatcher();        if (cm != null) {            if (!cm.doCredentialsMatch(token, info)) {                //not successful - throw an exception to indicate this:                String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";                throw new IncorrectCredentialsException(msg);            }        } else {            throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +                    "credentials during authentication.  If you do not wish for credentials to be examined, you " +                    "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");        }    }
采用的SpringBoot的方式配置,即java配置类注册方式实现,通过跟踪源码的方式getCredentialsMatcher()调用密码匹配器,注册自己的Hash散序管理器,SecurityManager回去调用,传入的数据库的password会与用户输入的密码加盐处理后对比,不正确会抛出异常信息。实现的方式如下,自定义密码匹对,注入到权限登录器

//配置自定义的权限登录器    @Bean(name="authRealm")    public AuthRealm authRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher,    EhCacheManager ehCacheManager) {        AuthRealm authRealm=new AuthRealm();        authRealm.setCredentialsMatcher(matcher);        authRealm.setCacheManager(ehCacheManager);        return authRealm;    }    /**     * 密码匹配凭证管理器     *      * @return     */    @Bean(name = "hashedCredentialsMatcher")    public HashedCredentialsMatcher hashedCredentialsMatcher() {        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();        hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 散列算法:这里使用MD5算法;        hashedCredentialsMatcher.setHashIterations(1024);// 散列的次数,比如散列两次,相当于                                                            // md5(md5(""));        return hashedCredentialsMatcher;    }

  private void cacheAuthenticationInfoIfPossible(AuthenticationToken token, AuthenticationInfo info) {        if (!isAuthenticationCachingEnabled(token, info)) {            log.debug("AuthenticationInfo caching is disabled for info [{}].  Submitted token: [{}].", info, token);            //return quietly, caching is disabled for this token/info pair:            return;        }        Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();        if (cache != null) {            Object key = getAuthenticationCacheKey(token);            cache.put(key, info);            log.trace("Cached AuthenticationInfo for continued authentication.  key=[{}], value=[{}].", key, info);        }    }

5.Reaml认证

  protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {        if (!realm.supports(token)) {            String msg = "Realm [" + realm + "] does not support authentication token [" +                    token + "].  Please ensure that the appropriate Realm implementation is " +                    "configured correctly or that the realm accepts AuthenticationTokens of this type.";            throw new UnsupportedTokenException(msg);        }        AuthenticationInfo info = realm.getAuthenticationInfo(token);        if (info == null) {            String msg = "Realm [" + realm + "] was unable to find account data for the " +                    "submitted AuthenticationToken [" + token + "].";            throw new UnknownAccountException(msg);        }        return info;    }

protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {        assertRealmsConfigured();        Collection<Realm> realms = getRealms();        if (realms.size() == 1) {            return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);        } else {            return doMultiRealmAuthentication(realms, authenticationToken);        }    }

6.认证信息处理

public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {        if (token == null) {            throw new IllegalArgumentException("Method argumet (authentication token) cannot be null.");        }        log.trace("Authentication attempt received for token [{}]", token);        AuthenticationInfo info;        try {            info = doAuthenticate(token);            if (info == null) {                String msg = "No account information found for authentication token [" + token + "] by this " +                        "Authenticator instance.  Please check that it is configured correctly.";                throw new AuthenticationException(msg);            }        } catch (Throwable t) {            AuthenticationException ae = null;            if (t instanceof AuthenticationException) {                ae = (AuthenticationException) t;            }            if (ae == null) {                //Exception thrown was not an expected AuthenticationException.  Therefore it is probably a little more                //severe or unexpected.  So, wrap in an AuthenticationException, log to warn, and propagate:                String msg = "Authentication failed for token submission [" + token + "].  Possible unexpected " +                        "error? (Typical or expected login exceptions should extend from AuthenticationException).";                ae = new AuthenticationException(msg, t);            }            try {                notifyFailure(token, ae);            } catch (Throwable t2) {                if (log.isWarnEnabled()) {                    String msg = "Unable to send notification for failed authentication attempt - listener error?.  " +                            "Please check your AuthenticationListener implementation(s).  Logging sending exception " +                            "and propagating original AuthenticationException instead...";                    log.warn(msg, t2);                }            }            throw ae;        }        log.debug("Authentication successful for token [{}].  Returned account [{}]", token, info);        notifySuccess(token, info);        return info;    }

 public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {        AuthenticationInfo info;        try {            info = authenticate(token);        } catch (AuthenticationException ae) {            try {                onFailedLogin(token, ae, subject);            } catch (Exception e) {                if (log.isInfoEnabled()) {                    log.info("onFailedLogin method threw an " +                            "exception.  Logging and propagating original AuthenticationException.", e);                }            }            throw ae; //propagate        }        Subject loggedIn = createSubject(token, info, subject);        onSuccessfulLogin(token, info, loggedIn);        return loggedIn;    }

7、登陆信息的保存至principals于session保存

 public void login(AuthenticationToken token) throws AuthenticationException {        clearRunAsIdentitiesInternal();        Subject subject = securityManager.login(this, token);        PrincipalCollection principals;        String host = null;        if (subject instanceof DelegatingSubject) {            DelegatingSubject delegating = (DelegatingSubject) subject;            //we have to do this in case there are assumed identities - we don't want to lose the 'real' principals:            principals = delegating.principals;            host = delegating.host;        } else {            principals = subject.getPrincipals();        }        if (principals == null || principals.isEmpty()) {            String msg = "Principals returned from securityManager.login( token ) returned a null or " +                    "empty value.  This value must be non null and populated with one or more elements.";            throw new IllegalStateException(msg);        }        this.principals = principals;        this.authenticated = true;        if (token instanceof HostAuthenticationToken) {            host = ((HostAuthenticationToken) token).getHost();        }        if (host != null) {            this.host = host;        }        Session session = subject.getSession(false);        if (session != null) {            this.session = decorate(session);        } else {            this.session = null;        }    }

最后回到控制器

public String loginUser(String username,String password,HttpSession session,    HttpServletRequest requset,boolean rememberMe) {    //加密处理,字符串处理    System.out.println(rememberMe);    username = username.trim();    UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(username,password);        Subject subject = SecurityUtils.getSubject();        try {            subject.login(usernamePasswordToken);   //完成登录            User user=(User) subject.getPrincipal();            session.setAttribute("user", user);            return "index";        }        catch(Exception e) {        requset.setAttribute("err", e.getMessage());            return "login";//返回登录页面        }            }