上篇讲了shiro是如何过滤请求链接,判断用户是否已经登录。
这篇就是讲解shiro用户登录时,如何把登录信息缓存起来,下次用户登录其他需要登录的链接时,如何判断已经登录了。
RetryLimitHashedCredentialsMatcher自定义的登录凭据,也就是登录的处理方案
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
private Logger logger = LoggerFactory.getLogger(RetryLimitHashedCredentialsMatcher.class);
private Cache<String, AtomicInteger> passwordRetryCache;
public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
passwordRetryCache = cacheManager.getCache("passwordRetryCache");
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
String username = (String)token.getPrincipal();
//retry count + 1
AtomicInteger retryCount = passwordRetryCache.get(username);
if(retryCount == null) {
retryCount = new AtomicInteger(0);
passwordRetryCache.put(username, retryCount);
}
if(retryCount.incrementAndGet() > 5) {
//if retry count > 5 throw
logger.info("===========尝试超过5次==============");
throw new ExcessiveAttemptsException();
}
boolean matches = super.doCredentialsMatch(token, info);
if(matches) {
//clear retry count
passwordRetryCache.remove(username);
}
return matches;
}
}
UserRealm:Realm:域,Shiro 从从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;
UserController:用户登录功能
@Controller
public class UserController {
@RequestMapping("DoLogin")
public ModelAndView login(String username,String password){
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
Subject subject = SecurityUtils.getSubject();
if(subject.isAuthenticated()){
return new ModelAndView("admin");
}
try {
subject.login(token);
if(subject.isAuthenticated()){
return new ModelAndView("admin");
}
}catch (Exception e){
}
return new ModelAndView("login");
}
}
源码请点击:https://github.com/smallleaf/cacheWeb
浏览器输入http://localhost:8080/DoLogin?username=admin&password=admin
断点打到DelegatingFilterProxy.doFilter。和UserController.login
我们走起
请求肯定是会先经过DelegatingFilterProxy进行shiro过滤的过滤功能的。和上篇一样。
这里我们分析,为什么没有登录的时候。subject.getPrincipal() ==null。
我们debug进入AbstractShiroFIlter.doFilterInternal。
final Subject subject = createSubject(request, response);
最后进入
public Subject createSubject(SubjectContext subjectContext) {
//create a copy so we don't modify the argument's backing map:
SubjectContext context = copy(subjectContext);
//ensure that the context has a SecurityManager instance, and if not, add one:
context = ensureSecurityManager(context);
//Resolve an associated Session (usually based on a referenced session ID), and place it in the context before
//sending to the SubjectFactory. The SubjectFactory should not need to know how to acquire sessions as the
//process is often environment specific - better to shield the SF from these details:
context = resolveSession(context);
//Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first
//if possible before handing off to the SubjectFactory:
context = resolvePrincipals(context);
Subject subject = doCreateSubject(context);
//save this subject for future reference if necessary:
//(this is needed here in case rememberMe principals were resolved and they need to be stored