zl程序教程

您现在的位置是:首页 >  前端

当前栏目

Shiro(二):Shiro 认证(xml配置方式)

认证配置XML 方式 shiro
2023-09-11 14:20:19 时间

Shiro 认证

一、相关概念

在这里插入图片描述

二、Shiro 认证流程

shiro认证流程:

1.获取当前的Subject。调用SecurityUtils.getSubject()方法;
2.测试当前的用户是否已经被认证。即是否已经登录。调用Subject的isAuthenticated()方法;
3.若没有被认证,则把用户名和密码封装为UsernamePasswordToken对象

 如何得到用户名和密码:
   1). 创建一个表单页面
   2). 把请求提交到SpringMVC 的Handler
   3). 获取用户名和密码

4.执行登录:调用Subject 的login(AuthenticationToken)方法;
5.自定义Realm的方法,从数据库中获取对应的记录,返回给Shiro;

自定义Realm的方法:
  1).实际上需要继承org.apache.shiro.realm.AuthenticaingRealm类
  2).并实现getAuthenticationInfo(AuthenticationToken)方法。

6.由shiro完成对密码的比对

三、实现认证流程

1.创建表单页面

login.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>login</title>
</head>
<body>
  <h3>login Page</h3>
  <form action="login" method="post">
    用户名:<input type="text" name="username"/><br><br>
    密    码:<input type="password" name="password"/><br><br>
    <input type="submit" value="submit"/>
  </form>
</body>
</html>

2.编写ShiroHandlerController 类

编写ShiroHandlerController 类,实现登录逻辑处理:

package com.example.shiro.controller;

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.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

@RestController
public class ShiroHandlerController {

	/**
	 * 进入login页面
	 * @return
	 */
	@RequestMapping("/toLogin")
	public ModelAndView login(){
		return new ModelAndView("login");
	}
	
	/**
	 * 进入list页面
	 * @return
	 */
	@RequestMapping("/list")
	public ModelAndView list(){
		return new ModelAndView("list");
	}
	
	/**
	 * 进入unauthorized页面
	 * @return
	 */
	@RequestMapping("/unauthorized")
	public ModelAndView unauthorized(){
		return new ModelAndView("unauthorized");
	}
	
	/**
	 * 登录
	 * @param username
	 * @param password
	 * @return
	 */
	@RequestMapping("/login")
	public ModelAndView login(@RequestParam("username")String username,@RequestParam("password")String password){

		//1.获取当前的Subject
		Subject subject=SecurityUtils.getSubject();
		//2.判断当前的用户是否已经被认证,即是否已经登录
		if(!subject.isAuthenticated()){
			//3.若没有被认证,则把username和password封装称UsernamePasswordToken对像
			UsernamePasswordToken token=new UsernamePasswordToken(username, password);
			//4.执行登录
			try {
				subject.login(token);
				System.out.println("登录成功");
			} 
			//所有认证时异常的父类
			catch (AuthenticationException e) {
				System.out.println("登录失败:"+e.getMessage());
				return new ModelAndView("login");
			}
		}
		return new ModelAndView("list");
	}
}

注意:不要忘记在配置文件,需要把登录请求链接放开。
在这里插入图片描述

3.实现Realm

实现过程:
1).把AuthenticationToken 转换称 UsernamePasswordToken
2).从UsernamePasswordToken中获取用户名
3).调用数据库方法,从数据库中查询username对应的用户记录
4).判断用户是否存在,若不存在,则抛出UnknownAccountException异常
5).根据用户信息的情况,决定是否需求抛出其他的AutheticationException异常。如用户锁定等
6).根据用户的情况,来构建AuthenticationInfo对像并返回

package com.example.shiro.realms;

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 ShiroRealm extends AuthenticatingRealm{

	/**
	 * 认证 
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("doGetAuthenticationInfo:"+token);
		//1.把AuthenticationToken 转换称 UsernamePasswordToken
		UsernamePasswordToken upToken =(UsernamePasswordToken) token;
		//2.从UsernamePasswordToken中获取用户名
		String username=upToken.getUsername();
		//3.调用数据库方法,从数据库中查询username对应的用户记录
		System.out.println("从数据库中获取username:"+username+"对应的用户信息");
		//4.判断用户是否存在,若不存在,则抛出UnknownAccountException异常
		if("unknown".equals(username)){
			throw new UnknownAccountException("用户不存在!");
		}
		//5.根据用户信息的情况,决定是否需求抛出其他的AutheticationException异常。如用户锁定等
		if("monster".equals(username)){
			throw new LockedAccountException("用户被锁定!");
		}
		//6.根据用户的情况,来构建AuthenticationInfo对像并返回,通常使用的实现类是SimpleAuthenticationInfo
		//以下信息是从数据库获取的
		//1).principal:认证的实体类信息。可以是username,也可以是数据表对应的用户的体类对象
		Object principal=username;
		//2).credentials:密码(数据库获取的用户的密码)
		Object credentials="123456";
		//3).realmName:当前realm对象的name,调用父类的getName()方法即可
		String realmName=getName();
		
		SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(principal, credentials, realmName);
		return info;
	}



}

四、测试

1.如果用户名输入unknown,密码任意,则”用户不存在“;
在这里插入图片描述

输入用户名unknown,密码1234,控制台输出异常信息:
在这里插入图片描述

2.如果用户名输入monster,密码任意,则”用户被锁定“;
在这里插入图片描述

输入用户名monster,密码1234,控制台输出异常信息:
在这里插入图片描述

3.输入除unknown和monster以外的用户名,密码输入123456,则认证成功(登录成功),否则认证失败(登录失败)
(1)用户名正确,密码错误的情况
在这里插入图片描述
输入用户名admin,密码1234,控制台输出异常信息,登录失败
在这里插入图片描述
(1)用户名和密码都正确的情况
在这里插入图片描述

输入用户名admin,密码123456,登录成功
在这里插入图片描述

在这里插入图片描述

注意:

输入用户名admin,密码123456,登录成功之后,返回到登录页面,输入其他的任意文字,依然可以进入到了list页面,控制台也没有打印任何信息,这是什么原因呢?
答:原因是 缓存。
想要解决这个问题,需要添加登出功能,来实现用户的切换登录。

如何实现登出?
答:(1)在list.jsp页面添加一个登出链接:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>list</title>
</head>
<body>
  
  <h3>list Page</h3>
  <div>欢迎登录</div>
  <a href="logout">logout</a>
</body>
</html>

(2)并把登出链接在applicationContext.xml文件的ShiroFilter过滤器中添加一个登出过滤即可
在这里插入图片描述