自学-Shiro的身份认证-05
来源:互联网 发布:淘宝卖家能屏蔽震动 编辑:程序博客网 时间:2024/04/30 04:01
学习了前几节,大家可能只是对Shiro有个大概的了解,其实,Shiro的重点及难点都在后面的博客中,接下来的这节我们来探讨一下身份认证.
我们可以一起来看下身份认证流程,有个大概的思绪,在来一起写代码进行实现。
身份认证流程:
流程步骤(借鉴英文文档翻译):
1.首先调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils. setSecurityManager()设置;
2.SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;
3.Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;
4.Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;
5.Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。
流程大概就这个样子,其实可以用更加通俗易懂的语言来描述更好。这些流程看源码是最清楚的,所以我们可以针对源码进行走一下,然后理解起来会更加的清楚明了。
代码进行一一解释
如下:
1.首先进行登录:
currentUser.login(token);2.SecurityManager一个大管家。
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; } }3.Authenticator核心的身份认证入口点。
/** * Delegates to the wrapped {@link org.apache.shiro.authc.Authenticator Authenticator} for authentication. */ public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException { return this.authenticator.authenticate(token); }
4.Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证。
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); } }
总而言之就是这样:
1.获取获取当前的 Subject. 调用 SecurityUtils.getSubject();
2.判断当前的用户是否已经被认证. 即是否已经登录. 调用 Subject 的 isAuthenticated() 。
3.若没有认证, 则把用户名和密码封装为 UsernamePasswordToken 对象。
4. 执行登录。调用 Subject 的login(token); 方法.
注:token指代是:AuthenticationToken
5.自定义 Realm 的方法, 从数据库中获取对应的记录,并对密码进行加密,来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo。
即:
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
实例结构:
实例代码:
LoginController.java:
package com.yiyi.controller;import javax.servlet.http.HttpServletRequest;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.subject.Subject;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;/** * 登录的Controller * @author hanyiyi * */@Controller@RequestMapping("/shiro")public class LoginController {/** * 登录方法 * @param request * @return */@RequestMapping(value="/login",method=RequestMethod.POST)public String login(HttpServletRequest request){//获取用户名和密码String username = request.getParameter("username");String password=request.getParameter("password"); // 使用SecurityUtils.getSubject();来获取当前的 Subject. Subject currentUser = SecurityUtils.getSubject(); //判断当前的用户是否已经被认证。 if(!currentUser.isAuthenticated()){ //若没被认证,则把用户名和密码封装为UsernamePasswordToken对象 UsernamePasswordToken token=new UsernamePasswordToken(username,password); token.setRememberMe(true); try { // 执行登录. currentUser.login(token); } // ... catch more exceptions here (maybe custom ones specific to your application? // 所有认证时异常的父类. catch (AuthenticationException ae) { //unexpected condition? error? System.out.println("登录失败---->"+ae.getMessage()); } } return "redirect:/index.jsp";}}
package com.yiyi.realm;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.LockedAccountException;import org.apache.shiro.authc.SimpleAuthenticationInfo;import org.apache.shiro.authc.UnknownAccountException;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.realm.AuthenticatingRealm;public class MyRealm extends AuthenticatingRealm{@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//将AuthenticationToken对象转换成UsernamePasswordToken对象 UsernamePasswordToken upToken = (UsernamePasswordToken) token; //获取UsernamePasswordToken中的用户名 String username = upToken.getUsername(); //从数据库中查询 username 对应的用户记录 System.out.println("从数据库中查找"+username+"的信息"); //若用户的信息不存在,则抛出UnknownAccountException异常。 if("unknown".equals(username)){ throw new UnknownAccountException("用户不存在"); }//根据用户的信息进行反馈,则抛出LockedAccountException异常。 if("han".equals(username)){ throw new LockedAccountException("用户被锁定"); } //根据用户的信息来封装SimpleAuthenticationInfo对象。 //当前 realm 对象的 nameString realmName = getName();//认证的实体信息。Object principal = username;//密码Object credentials="123456";SimpleAuthenticationInfo info =new SimpleAuthenticationInfo(principal, credentials, realmName);return info;}}
登录的页面:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <base href="<%=basePath%>"> <title>登录页面</title><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"> </head> <body> <h4>login page</h4> <form action="shiro/login" method="post"> username:<input type="text" name="username"> <br/><br/> password:<input type="password" name="password"><br/><br/> <input type="submit" value="提交"> </form> </body></html>拦截器配置;
先让shiro/login进行匿名访问:
图形化界面;
现在我们来执行下这个操作:
首先进入登录页面:
http://localhost:8080/Shiro-03/login.jsp
根据代码,我们先输入一个正确的用户名和密码:zhao 123456 这时候会进入到对应的页面,假如我们在一次进行入到登录页面,随意输入个错误的密码,这时候还是可以登录上的,这个是为什么呢?
原因是:shiro的缓存起到了作用,这时候避免出现这样的现象我们直接在applicationContext.xml中配置一个logou的拦截器即可。
index.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"> </head> <body> index jsp <a href="shiro/logout">登出</a> </body></html>
applicationContext.xml:
这其中还有很多重点需要一一解释:
①:为什么自定义的Realm 为什么直接继承AuthenticatingRealm呢?
②:SimpleAuthenticationInfo 这个对象中的参数都指代什么呢?
③:shiro的密码怎么进行比对呢,怎么进行加密呢?
这些都到下一节进行详细的描述吧。
Ps:新手自学,哪里不对还望指出,谢谢。
- 自学-Shiro的身份认证-05
- Shiro 的身份认证
- Shiro身份认证的流程
- Shiro 的身份认证-Realm
- 通过Shiro完成基础的身份认证
- 身份认证(shiro)
- shiro身份认证过程
- shiro身份认证
- shiro身份认证
- Shiro身份认证
- Shiro ---身份认证、授权
- Shiro身份认证
- 【shiro】--- 身份认证
- Shiro身份认证
- Apache Shiro 身份认证例子
- Apache Shiro 身份认证例子
- 用Shiro实现身份认证
- shiro身份认证(第一章)
- 数据库按子串排序
- PHP入门 第6章 语言结构语句
- HibernateTemplate 自动事务
- Redis安装
- 第十五周 字符串比较
- 自学-Shiro的身份认证-05
- 微信小程序lianc++后台
- MySQL中根据format字符串格式化date类型字段值
- Redis 3.2.4配置文件翻译
- Unity3D小游戏 -BallGame
- Android - Android实现定时器的方法
- html中DTD使用小结
- jfinal 多个数据库连接及使用
- tornado学习笔记(二):如何在tornado中以异步的方式调用同步函数