/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package net.noday.core.security; import java.io.Serializable; import javax.annotation.PostConstruct; import net.noday.core.security.CaptchaUsernamePasswordToken; import net.noday.core.security.IncorrectCaptchaException; import net.noday.core.service.SecurityService; import net.noday.core.utils.Captcha; import net.noday.core.utils.Digests; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.shiro.SecurityUtils; 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.authc.credential.AllowAllCredentialsMatcher; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.codec.Base64; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.session.Session; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ByteSource; /** * cat ShiroDbRealm * * @author <a href="http://www.noday.net">Noday</a> * @version , 2012-11-24 * @since */ public class ShiroDbRealm extends AuthorizingRealm { public static final String LOGINFAILEDCOUNTKEY = "longin_failed_count"; protected SecurityService<Loginable<Long>> service; private CredentialsMatcher hashedCredentialsMatcher; @SuppressWarnings("unused") private CredentialsMatcher allowAllCredentialsMatcher; public void setService(SecurityService<Loginable<Long>> service) { this.service = service; } /** * 认证回调函数,登录时调用. */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { if (authcToken instanceof CaptchaUsernamePasswordToken) { setCredentialsMatcher(hashedCredentialsMatcher); CaptchaUsernamePasswordToken token = (CaptchaUsernamePasswordToken) authcToken; String captcha = null; // Object s_count = getSession().getAttribute(LOGINFAILEDCOUNTKEY); Object s_captcha = getSession().getAttribute(Captcha.CAPTCHAKEY); if (s_captcha != null && s_captcha instanceof String) { captcha = (String) s_captcha; } if (!StringUtils.equalsIgnoreCase(captcha, token.getCaptcha())) { throw new IncorrectCaptchaException("验证码错误"); } Loginable<Long> user = service.findUserByLoginName(token.getUsername()); if (user != null) { return new SimpleAuthenticationInfo(new ShiroUser(user.getId(), user.getLoginName(), user.getName()),// 为什么不直接用User呐? user.getPassword(), ByteSource.Util.bytes(Base64.decode(user.getSalt())), getName()); } } else { setCredentialsMatcher(hashedCredentialsMatcher); UsernamePasswordToken token = (UsernamePasswordToken) authcToken; Loginable<Long> user = service.findUserByLoginName(token.getUsername()); return new SimpleAuthenticationInfo(new ShiroUser(user.getId(), user.getLoginName(), user.getName()), user.getPassword(), ByteSource.Util.bytes(Base64.decode(user.getSalt())), getName()); } return null; } /** * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用. */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal(); Loginable<Long> user = service.findUserByLoginName(shiroUser.loginName); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addRole(user.getRole()); // info.addRoles(user.getRoles()); // info.addStringPermissions(user.getPermissions()); return info; } /** * 设定Password校验的Hash算法与迭代次数. */ @PostConstruct public void initCredentialsMatcher() { HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(Digests.HASH_ALGORITHM); matcher.setHashIterations(Digests.HASH_INTERATIONS); matcher.setStoredCredentialsHexEncoded(false); // matcher.setHashSalted(true); {// 根据不同登录来源选择不同的matcher hashedCredentialsMatcher = matcher; allowAllCredentialsMatcher = new AllowAllCredentialsMatcher(); } // setCredentialsMatcher(matcher); } public void loginWithoutCredentials(AuthenticationToken authcToken) { getSubject().login(authcToken); } protected Session getSession() { return getSubject().getSession(); } protected Subject getSubject() { return SecurityUtils.getSubject(); } /** * 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息. */ public static class ShiroUser implements Serializable { /** * */ private static final long serialVersionUID = -3350074487024599768L; public Long id; public String loginName; public String name; public ShiroUser(Long id, String loginName, String name) { this.id = id; this.loginName = loginName; this.name = name; } public Long getId() { return this.id; } public String getName() { return name; } public String getLoginName() { return loginName; } /** * 本函数输出将作为默认的<shiro:principal/>输出. */ @Override public String toString() { return loginName; } /** * 重载equals,只计算loginName; */ @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this, "loginName"); } /** * 重载equals,只比较loginName */ @Override public boolean equals(Object obj) { return EqualsBuilder.reflectionEquals(this, obj, "loginName"); } } }