场景一:集群中各节点登录状态保持一致,当然这个集群没有统一的认证中心
场景二:微信环境微店项目,自动登录,进入微店,则判断用户是否注册,如果已经注册则自动登录否则跳转到注册页面(ps:需要用户关注公众号才可以注册)
这儿针对场景二开展
在shiro中自定义过滤器authcUser和tryLogin
authcUser 只有登录才可以访问当前路径,没有登录则尝试自动登录,登录失败跳转到注册loginUrl指向的页面
tryLogin 不需要登录也可以访问,如果没有登录则尝试自动登录
其实本来使用Servlet的Filter实现的,结果发现有点问题所以换成了shiro实现
spring-shiro.xml的部分配置文件
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager" />
<!-- 要求登录时的链接(登录页面地址),非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 -->
<property name="loginUrl" value="${http.domain.ssl}/register" />
<!-- 登录成功后要跳转的连接(本例中此属性用不到,因为登录成功后的处理逻辑在LoginController里硬编码) -->
<!-- <property name="successUrl" value="/" ></property> -->
<!-- 用户访问未对其授权的资源时,所显示的连接 -->
<property name="unauthorizedUrl" value="/error/unauthorized" />
<property name="filters">
<map>
<entry key="authcUser" >
<bean class="com.shiro.filter.AuthenticationFilter"></bean>
</entry>
<entry key="tryLogin">
<bean class="com.shiro.filter.AnonAndTryLoginFilter"></bean>
</entry>
</map>
</property>
<property name="filterChainDefinitions">
<value>
/my/comeshare = anon
/my/share = anon
/my/* = authcUser
/team/* = authcUser
/** = tryLogin
</value>
</property>
</bean>
自定义的BaseFilter
public abstract class AutoLoginBaseFilter extends org.apache.shiro.web.filter.authc.AuthenticatingFilter{
Logger log = LoggerFactory.getLogger(AutoLoginBaseFilter.class);
@Autowired
protected UserProcess userProcess;
@Autowired
protected IismsWxSubscribeInfoService subscribeInfoService;
@Override
protected AuthenticationToken createToken(ServletRequest request,
ServletResponse response) throws Exception {
HttpServletRequest req = (HttpServletRequest)request;
//从请求的session中获取openId
String openId = SecurityHelper.getOpenIdFromRequest(req);
if (!StringUtils.isBlank(openId)) {
IsmsWxSubscribeInfo wxSubscribeInfo = subscribeInfoService.selectSubscribeInfoByOpenId(openId);
IsmsCusUserInfo user = userProcess.getByOpenId(openId);
//如果用户已经关注切是注册状态则创建Token
if (wxSubscribeInfo != null && "1".equals(wxSubscribeInfo.getActionType())
&& RegisterStatusEnum.YES.getValue().equals(user.getRegisterStatus())) {
return new UsernamePasswordToken(user.getMobilePhone(), user.getPassword());
}
}
return null;
}
@Override
protected boolean onAccessDenied(ServletRequest arg0, ServletResponse arg1)
throws Exception {
return false;
}
/**
* 尝试自动登录
* 关注微信且用户状态是未注册的才可以登录成功
*
* @param request
* @param response
* @return true 登录成功 否则失败
*/
@SuppressWarnings("deprecation")
public boolean tryLogin(ServletRequest request, ServletResponse response) {
ApiErrorCodeEnum result = ApiErrorCodeEnum.USER_0018;
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse res = (HttpServletResponse)response;
String openId = SecurityHelper.getOpenIdFromRequest(req);
//关注微信且用户状态是未注册的才可以登录成功
if (!StringUtils.isBlank(openId) && !SecurityUtils.getSubject().isAuthenticated()) {
IsmsWxSubscribeInfo wxSubscribeInfo = subscribeInfoService.selectSubscribeInfoByOpenId(openId);
IsmsCusUserInfo user = userProcess.getByOpenId(openId);
if (wxSubscribeInfo != null && "1".equals(wxSubscribeInfo.getActionType())
&& RegisterStatusEnum.YES.getValue().equals(user.getRegisterStatus())) {
//尝试自动登录
result = userProcess.login(user.getId(), user.getPassword(), req, res);
}
}
return result == null;
}
}
//定义过滤器,不需要登录即可访问,在这个过滤器中尝试去登录,即使登录不成功也通过
public class AnonAndTryLoginFilter extends AutoLoginBaseFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request,
ServletResponse response, Object mappedValue) {
if (!SecurityUtils.getSubject().isAuthenticated()) {
tryLogin(request, response);
}
return true;
}
}
//定义过滤器,如果没有登录自动登录,登录成功则通过否则跳转到注册页面
public class AuthenticationFilter extends AutoLoginBaseFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request,
ServletResponse response, Object mappedValue) {
boolean authed = SecurityUtils.getSubject().isAuthenticated();
if (!authed) {
// 如果已经注册的用户,肯定能自动登录成功,登陆不成功则肯定没有注册
tryLogin(request, response);
}
authed = SecurityUtils.getSubject().isAuthenticated();
try {
if (!authed) {
//没有注册的用户引导去注册页面
org.apache.shiro.web.util.WebUtils.issueRedirect(request, response, getLoginUrl());
}
} catch (IOException e) {
}
return authed;
}
}
//UserProcess 中的login方法,自动登录的关键代码
PrincipalCollection principals = new SimplePrincipalCollection(user.getId(), "MobileRealm");
Builder builder = new WebSubject.Builder(request, response);
builder.principals(principals);
builder.authenticated(true);
WebSubject subject = builder.buildWebSubject();
ThreadContext.bind(subject);