Springboot+shiro学习一(身份认证)实现登陆认证
shiro看了有一段时间了。但是由于之前对这部分理解不了所以在这上面学习的进展一直不多。但是有了解权限管理在日常开发中很重要,所以硬着头皮也要啃下来。
实现功能:
- 所有界面需要登陆后才能查看
shiro的四大组件:
- 身份认证(Authentication)-证明用户身份,通常叫做登陆(login)。
- 授权(Authorization)-访问控制
- 加密(Cryptography)-保护或隐藏数据
- 会话管理(session management)每个用户时间敏感状态
这里面只用到了身份认证,权限认证只用到了一点点,shiro的原理是封装的过滤器,他能够在访问浏览器前能过自动完成一些内容。
shiro配置主要两部分——shiroconfig和自定义的Realm(继承AuthorizingRealm)。其中,shiroconfig是shiro的主要配置文件,而自定义的Realm主要是重写AuthorizingRealm的两个方法,分别是身份认证和授权认证调用数据库查询比对。
项目结构:
环境:
- 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>
页面目录,随便写:
现在切入正题。
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";
}
}
}
运行测试:访问其他接口都被返回到这个界面
登陆成功后,界面可以访问
到此,用户认证就完成了,到后续会再此基础上添加shiro的权限认证等等。