Shrio功能应用(一)--登陆验证(源码)以及权限执行过程

大致过程

     用前台的用户名、明文密码等信息创建验证的token令牌对象
     执行subject.login(token);登陆时,会跳转到自定义的Realm的认证方法,一般在认证方法里面查询数据库,获取数据库对应的user信息(包括加密后的密码),将user信息封装到info对象里
     Shiro内部验证的时候,会将token的明文密码加密后,与info里user的密码(也就是数据库中用户密码)进行比较,结果一致则返回true,登陆成功

执行过程

     1 .前台页面登陆后,执行登陆验证方法subject.login(token);实际调用的是securityManager.login(this, token)
在这里插入图片描述
     1-1.securityManager.login(this, token)方法里调用 authenticate(token);方法获取需要匹配的AuthenticationInfo info对象
在这里插入图片描述
     1-2.authenticate方法里调用doAuthenticate(token);来获取需要匹配的AuthenticationInfo info对象
在这里插入图片描述
     1-3.在doAuthenticate方法里面,会获取自定义的Realm,本例只设置一个Realm,所以跳转单Realm的认证方法doSingleRealmAuthentication
在这里插入图片描述
     1-4.在doSingleRealmAuthentication方法里,会跳转getAuthenticationInfo方法:
在这里插入图片描述
     1-5.在getAuthenticationInfo方法中,先从缓存中获取匹配的AuthenticationInfo info对象,本例没有设置缓存,所以往下通过自定义Realm的doGetAuthenticationInfo(token)方法获取info对象:
在这里插入图片描述
     1-6.往下会跳转到自定义Realm重写的doGetAuthenticationInfo方法里。方法返回SimpleAuthenticationInfo的info对象,SimpleAuthenticationInfo(,,,,)有四个参数:
          1).第一个Object principal,可以是用户名,也可以是user对象
          2).第二个Object hashedCredentials,需要认证的密码,前台输入的密码会和此密码匹配
          3).第三个ByteSource credentialsSalt,加密认证是的salt
          4).第四个String realmName,就是realm的名字,即shiroConfig配置的relam
在这里插入图片描述
     1-7.执行完自定义Realm获得info对象,info对象包含了数据库的密码(加密过的密码),然后执行assertCredentialsMatch(token, info);进行密码验证:
在这里插入图片描述
     1-8.进入密码验证方法assertCredentialsMatch先获取密码比较器,然后再调用密码比较方法doCredentialsMatch:
在这里插入图片描述
     1-9.在doCredentialsMatch方法里,hashProvidedCredentials(token, info);方法主要是将前台的明文密码通过加密算法SimpleHash后,获取加密后的密码。
在这里插入图片描述
在这里插入图片描述
     1-10.前台明文密码加密后密码tokenHashedCredentials与自定义Realm从数据库中获取的加密密码进行匹配:
在这里插入图片描述
     1-11.匹配成功subject.login(token);返回true;
在这里插入图片描述
     2.登陆成功重定向到userList.do的控制器,进行成功页面跳转:
在这里插入图片描述
     2-1.userList页面中使用了shiro:hasPermission标签做权限控制,页面有三个shiro:hasPermission权限标签,则会执行三次自定义Realm里的授权方法doGetAuthorizationInfo:
在这里插入图片描述
     2-2.具体源码没怎么看,但主要和验证差不多,都是先从缓存取授权的info对象,没有再跑自定义Realm的授权方法去获取info对象。我想大概就是shiro:hasPermission标签的值和查询数据库获取的权限列表perms进行匹配,不涉及加密,匹配成功则显示标签内容:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

代码示例

      代码地址:
          https://github.com/OooooOz/SpringBoot-Shiro

      前台登陆页面:

<body>
	<h2>登录页面</h2>
	<form action="login.do" method="post" name="loginForm">
		用户名称:<input type="text" name="username" value=""><br>
		用户密码:<input type="password" name="password" value=""><br>
		<input type="submit" name="log" value="登录"/>
		<input type="checkbox" name="rememberMe" />记住我<br/>
	</form>
	<a href="/unlockAccount.do">解锁admin用户</a></button>
	<div th:text="${msg}"></div>
</body>

     控制器Controller:

@Controller
public class LoginController {

	@Resource
	IUserService userService;

	@RequestMapping("/toLogin.do")
	public String toLogin() {
		return "login";
	}

	@RequestMapping("/login.do")
	public String login(String username, String password,boolean rememberMe, Model model) {
		// 判断用户名密码是否为空
		if (username != null && !"".equals(username) && password != null && !"".equals(password)) {
			// 1.获取subject
			Subject subject = SecurityUtils.getSubject();
			// 2.创建验证用的令牌对象
			UsernamePasswordToken token = new UsernamePasswordToken(username, password,rememberMe);
			try {
				//3.登陆认证
				subject.login(token);
				boolean flag = subject.isAuthenticated();	//判断是否通过认证
				//4.认证结果处理
				if(flag){
					System.out.println("登录成功!");
					User user = (User)subject.getPrincipal();
					model.addAttribute("user",user);
					return "redirect:userList.do";
				}else{
					model.addAttribute("msg","登录认证失败!");
					return "login";
				}
			
			} catch (Exception e) {
				model.addAttribute("msg","登录认证失败!");
				if (e instanceof LockedAccountException) {
					model.addAttribute("msg","失败次数过多,用户已锁定,五分钟后再试");
				}
			}
		}
		return "login";
	}
	@RequestMapping("/userList.do")
	//@ResponseBody
	public String userList(Model model){
		//查询所有的用户信息并且显示到页面上
		Subject subject = SecurityUtils.getSubject();
		System.out.println("==============是否通过认证:"+subject.isAuthenticated());
		System.out.println("==============是否记住我:"+subject.isRemembered());
		List<User> list = userService.findAll();
		model.addAttribute("userList", list);
		return "userList";
	}
	@RequestMapping("/unFunc.do")
	public String noFunc(){
		return "unFunc";
	}

	@RequestMapping("/logout.do")
	public String logout(){
		//清除session
		return "login";
	}
}

     ShiroConfig的过滤器:

	@Bean
	public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);
        //loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证
		shiroFilterFactoryBean.setLoginUrl("/toLogin.do");
        //通过unauthorizedUrl指定没有权限操作时跳转页面
		shiroFilterFactoryBean.setUnauthorizedUrl("/unFunc.do");

		//-----------------------------过虑器链定义------------------------------//
        LinkedHashMap<String, String> perms = new LinkedHashMap<>();
        perms.put("/login.do","anon");
        //其他资源都需要认证  authc 表示需要认证才能进行访问 user表示配置记住我或认证通过可以访问的地址
        perms.put("/*", "authc");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(perms);  //把权限过滤map设置shiroFilterFactoryBean
		return shiroFilterFactoryBean;
	}

     自定义Realm:

	/**
	 * 认证方法
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("执行了认证方法。。。。");
		String username = token.getPrincipal().toString();
		System.out.println("username:" + username);
		//需要通过用户名查询用户密码
		User user = userService.findByUsername(username);
		//把user对象封装的AuthenticationInfo中返回
		SimpleAuthenticationInfo authenticationInfo = 
				new SimpleAuthenticationInfo(user,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),"shiroRealm");
		return authenticationInfo;
	}
	

     登陆成功跳转页面:

<table>
	<caption>
		<div shiro:hasPermission="function:add"><a href="/toAdd.do">添加</a></div>
		<div shiro:hasPermission="function:update"><a href="/toUpdate.do">修改</a></div>
		<div shiro:hasPermission="function:delete"><a href="/toDelete.do">删除</a></div>
	</caption>
	<tr>
		<td>用户ID</td><td>用户名称</td><td>电话</td><td>email</td><td>状态</td><td>创建时间</td><td>备注</td>
	</tr>
	<tr th:each="user : ${userList}">
		<td th:text="${user.userId}"></td>
		<td th:text="${user.userName}"></td>
		<td th:text="${user.phone}"></td>
		<td th:text="${user.email}"></td>
		<td th:if="${user.status == 1 ?'正常':'异常'}"></td>
		<td th:text="${user.createTime}"></td>
		<td th:text="${user.note}"></td>
	</tr>
</table>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章