Springboot+shiro学习一(身份认证)实现登陆认证

shiro看了有一段时间了。但是由于之前对这部分理解不了所以在这上面学习的进展一直不多。但是有了解权限管理在日常开发中很重要,所以硬着头皮也要啃下来。

实现功能:

  • 所有界面需要登陆后才能查看

github地址

shiro的四大组件:

  • 身份认证(Authentication)-证明用户身份,通常叫做登陆(login)。
  • 授权(Authorization)-访问控制
  • 加密(Cryptography)-保护或隐藏数据
  • 会话管理(session management)每个用户时间敏感状态

这里面只用到了身份认证,权限认证只用到了一点点,shiro的原理是封装的过滤器,他能够在访问浏览器前能过自动完成一些内容。

shiro配置主要两部分——shiroconfig和自定义的Realm(继承AuthorizingRealm)。其中,shiroconfig是shiro的主要配置文件,而自定义的Realm主要是重写AuthorizingRealm的两个方法,分别是身份认证和授权认证调用数据库查询比对。

项目结构:Springboot+shiro学习一(身份认证)实现登陆认证

环境:

  • Springboot2
  • mybatis
  • shiro

新建表:

/*
Navicat MySQL Data Transfer

Source Server         : localhost_3306
Source Server Version : 80011
Source Host           : localhost:3306
Source Database       : shiro

Target Server Type    : MYSQL
Target Server Version : 80011
File Encoding         : 65001

Date: 2019-03-10 09:04:59
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(10) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `perms` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('6', '66', '66', 'add');
INSERT INTO `user` VALUES ('5', 'bigsao', '55', 'update');

对应的bean:

public class User {
	private Integer id;
	private String name;
	private String password;
	private String perms;
	public String getPerms() {
		return perms;
	}
	public void setPerms(String perms) {
		this.perms = perms;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	
}

mybatis完成简单查询:

import com.example.shiro.bean.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {

	public User findByName(String name);
	
	public User findById(Integer id);
}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 该文件存放CRUD的sql语句 -->
<mapper namespace="com.example.shiro.mapper.UserMapper">
	
	<select id="findByName" parameterType="string" resultType="User">
	select *
		FROM 
		user where name = #{name}
	</select>
	
	<select id="findById" parameterType="int" resultType="User">
		SELECT *
		FROM 
		user where id = #{id}
	</select>
</mapper>

给出连个主要html,其他根据目录自己随便

login页面:

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h3>登录</h3>
<h3 th:text="${msg}" style="color: red"></h3>

<form method="post" action="login">
	用户名:<input type="text" name="name"/><br/>
	密码:<input type="password" name="password"/><br/>
	<input type="submit" value="登录"/>
</form>
</body>
</html>

test.html

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>测试Thymeleaf的使用</title>
</head>
<body>
<h3 th:text="${name}"></h3>

<hr/>
<div >
进入用户添加功能: <a href="add">用户添加</a><br/>
</div>
<div >
进入用户更新功能: <a href="update">用户更新</a><br/>
</div>
<a href="toLogin">登录</a>
</body>
</html>

页面目录,随便写:
Springboot+shiro学习一(身份认证)实现登陆认证

现在切入正题。

UserRealm.java

import com.example.shiro.bean.User;
import com.example.shiro.mapper.UserMapper;
import org.apache.juli.logging.LogFactory;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;



/**
 * 自定义Realm
 * @author lenovo
 *
 */
public class UserRealm extends AuthorizingRealm{

	@Autowired (required = false)
	private UserMapper userMapper;
	private final Logger logger= LoggerFactory.getLogger(UserRealm.class);
	/**
	 * 执行授权逻辑
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		System.out.println("执行授权逻辑");
		
		//给资源进行授权
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		//不授权先不写		
		return info;
	}
	


	/**
	 * 执行认证逻辑
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
		System.out.println("执行认证逻辑");
		
		//编写shiro判断逻辑,判断用户名和密码
		//1.判断用户名
		UsernamePasswordToken token = (UsernamePasswordToken)arg0;
		
		User user = userMapper.findByName(token.getUsername());
		if(user==null){
			//用户名不存在
			return null;//shiro底层会抛出UnKnowAccountException
		}
		//2.判断密码
		return new SimpleAuthenticationInfo(user,user.getPassword(),"");
	}

}

shiroconfig:shiro的主要配置

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;



/**
 * Shiro的配置类
 * @author lenovo
 *
 */
@Configuration
public class ShiroConfig {

	/**
	 * 创建ShiroFilterFactoryBean
	 */
	@Bean
	public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		
		//设置安全管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		
		//添加Shiro内置过滤器
		/**
		 * Shiro内置过滤器,可以实现权限相关的拦截器
		 *    常用的过滤器:
		 *       anon: 无需认证(登录)可以访问
		 *       authc: 必须认证才可以访问
		 *       user: 如果使用rememberMe的功能可以直接访问
		 *       perms: 该资源必须得到资源权限才可以访问
		 *       role: 该资源必须得到角色权限才可以访问
		 */
		Map<String,String> filterMap = new LinkedHashMap<String,String>();
		//放行login.html页面
		filterMap.put("/login", "anon");//要将登陆的接口放出来,不然没权限访问登陆的接口
//
		//授权过滤器
		//注意:当前授权拦截后,shiro会自动跳转到未授权页面
		filterMap.put("/*", "authc");//authc即为认证登陆后即可访问
		
		//修改调整的登录页面
		shiroFilterFactoryBean.setLoginUrl("/toLogin");
		//设置未授权提示页面
		shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
		
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
		
		
		return shiroFilterFactoryBean;
	}
	
	/**
	 * 创建DefaultWebSecurityManager
	 */
	@Bean(name="securityManager")
	public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		//关联realm
		securityManager.setRealm(userRealm);
		return securityManager;
	}
	
	/**
	 * 创建Realm
	 */
	@Bean(name="userRealm")
	public UserRealm getRealm(){
		return new UserRealm();
	}

}

Usercontroller:

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;



@Controller
public class UserController {

	/**
	 * 测试方法
	 */
	@RequestMapping("/hello")
	@ResponseBody
	public String hello(){
		System.out.println("UserController.hello()");
		return "ok";
	}
	
	@RequestMapping("/add")
	public String add(){
		return "/user/add";
	}
	
	@RequestMapping("/update")
	public String update(){
		return "/user/update";
	}
	
	@RequestMapping("/toLogin")
	public String toLogin(){
		return "/login";
	}
	
	@RequestMapping("/noAuth")
	public String noAuth(){
		return "/noAuth";
	}

	/**
	 * 测试thymeleaf
	 */
	@RequestMapping("/testThymeleaf")
	public String testThymeleaf(Model model){
		//把数据存入model
		model.addAttribute("name", "shirotest");
		//返回test.html
		return "test";
	}
	
	/**
	 * 登录逻辑处理
	 */
	@RequestMapping("/login")
	public String login(String name,String password,Model model){
		System.out.println("name="+name);
		/**
		 * 使用Shiro编写认证操作
		 */
		//1.获取Subject
		Subject subject = SecurityUtils.getSubject();
		
		//2.封装用户数据
		UsernamePasswordToken token = new UsernamePasswordToken(name,password);
		
		//3.执行登录方法
		try {
			subject.login(token);
			
			//登录成功
			//跳转到test.html
			return "redirect:/testThymeleaf";
		} catch (UnknownAccountException e) {
			//e.printStackTrace();
			//登录失败:用户名不存在
			model.addAttribute("msg", "用户名不存在");
			return "login";
		}catch (IncorrectCredentialsException e) {
			//e.printStackTrace();
			//登录失败:密码错误
			model.addAttribute("msg", "密码错误");
			return "login";
		}
	}
}

运行测试:访问其他接口都被返回到这个界面
Springboot+shiro学习一(身份认证)实现登陆认证
Springboot+shiro学习一(身份认证)实现登陆认证

登陆成功后,界面可以访问
Springboot+shiro学习一(身份认证)实现登陆认证
Springboot+shiro学习一(身份认证)实现登陆认证
到此,用户认证就完成了,到后续会再此基础上添加shiro的权限认证等等。