shiro安全框架:
两大核心功能:认证,授权
需求描述
有三个用户,root ,blog ,user
root拥有所有权限,blog只能进入blog ,user只能进入user
登录页面
前台登录界面,输入正确的用户名密码可以进入首页。
密码错误
用户不存在
首页
首页root登录效果。拥有操作博客,用户的权限。
blog用户登录:只能看到有权限的页面入口
需求实现:
1.表数据建立
/**
四个表
user role Permission
user_role role_permession
user和role是多对多的关系
role和permission也是多对多关系
*/
/**
四个表
user role Permission
user_role role_permession
user和role是多对多的关系
role和permission也是多对多关系
*/
---用户
CREATE TABLE USER(
userId INT PRIMARY KEY AUTO_INCREMENT ,
userName VARCHAR(32) ,
PASSWORD VARCHAR(64)
)ENGINE INNODB DEFAULT CHARSET=utf8;
---角色
CREATE TABLE role(
roleId INT PRIMARY KEY AUTO_INCREMENT ,
roleName VARCHAR(32)
)ENGINE INNODB DEFAULT CHARSET=utf8;
--权限
CREATE TABLE Permission(
pId INT PRIMARY KEY AUTO_INCREMENT ,
pName VARCHAR(32)
)ENGINE INNODB DEFAULT CHARSET=utf8;
--用户角色对应
CREATE TABLE user_role(
urId INT,
userId INT ,
roleId INT
)ENGINE INNODB DEFAULT CHARSET=utf8;
---角色权限对应关系表
CREATE TABLE role_permession(
rpId INT,
pId INT ,
roleId INT
)ENGINE INNODB DEFAULT CHARSET=utf8;
---添加用户
INSERT INTO USER (username,PASSWORD) VALUES('root','1');
---添加外键
ALTER TABLE user_role ADD
CONSTRAINT FK_user_id FOREIGN KEY(userId) REFERENCES USER(userId) ON DELETE NO ACTION ON UPDATE NO ACTION
---添加外键
ALTER TABLE user_role ADD
CONSTRAINT FK_role_id FOREIGN KEY(roleId) REFERENCES role(roleId) ON DELETE NO ACTION ON UPDATE NO ACTION
--INSERT 预置数据 permission
INSERT INTO `permission` (`pId`, `pName`) VALUES('1','/admin/**');
INSERT INTO `permission` (`pId`, `pName`) VALUES('2','/user/**');
INSERT INTO `permission` (`pId`, `pName`) VALUES('3','/blog/**');
---role
INSERT INTO `role` (`roleId`, `roleName`) VALUES('1','admin');
INSERT INTO `role` (`roleId`, `roleName`) VALUES('2','user');
INSERT INTO `role` (`roleId`, `roleName`) VALUES('3','blog');
--user_role
INSERT INTO `user_role` (`urId`, `userId`, `roleId`) VALUES('1','1','1');
INSERT INTO `user_role` (`urId`, `userId`, `roleId`) VALUES('2','1','2');
INSERT INTO `user_role` (`urId`, `userId`, `roleId`) VALUES('3','2','2');
INSERT INTO `user_role` (`urId`, `userId`, `roleId`) VALUES('4','3','3');
INSERT INTO `user_role` (`urId`, `userId`, `roleId`) VALUES('4','1','3');
--role_permession
INSERT INTO `role_permession` (`rpId`, `pId`, `roleId`) VALUES('1','1','1');
INSERT INTO `role_permession` (`rpId`, `pId`, `roleId`) VALUES('2','2','2');
INSERT INTO `role_permession` (`rpId`, `pId`, `roleId`) VALUES('3','3','3');
----根据USER NAME 查询
SELECT *
FROM USER u, role r, user_role ur, role_permession rp , permission p
WHERE u.userName = 'blog'
AND u.userId = ur.userId
AND ur.roleId = r.roleId
AND r.roleId = rp.roleId
AND rp.pId = p.pId;
maven依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wang</groupId>
<artifactId>shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shiro</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.2</version>
</dependency>
<!-- shiro 整合 thymeleaf-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
实体类
User
package com.wang.pojo;
import java.util.ArrayList;
import java.util.HashSet;
public class User {
private int userId;
private String userName;
private String password;
private ArrayList<Role> roles;
public int getUserId() {
return userId;
}
public User() {
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public ArrayList<Role> getRoles() {
return roles;
}
public void setRoles(ArrayList<Role> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", password='" + password + '\'' +
", roles=" + roles +
'}';
}
public User(int userId, String userName, String password, ArrayList<Role> roles) {
this.userId = userId;
this.userName = userName;
this.password = password;
this.roles = roles;
}
}
Permission
package com.wang.pojo;
import java.util.HashSet;
public class Permission {
private int pId;
private String pName;
public Permission() {
}
public Permission(int pId, String pName) {
this.pId = pId;
this.pName = pName;
}
public int getpId() {
return pId;
}
public void setpId(int pId) {
this.pId = pId;
}
public String getpName() {
return pName;
}
public void setpName(String pName) {
this.pName = pName;
}
@Override
public String toString() {
return "Permission{" +
"pId=" + pId +
", pName='" + pName + '\'' +
'}';
}
}
Role
package com.wang.pojo;
import java.util.ArrayList;
public class Role {
private int roleId;
private String roleName;
//一对多
private ArrayList<Permission> permissions;
public int getRoleId() {
return roleId;
}
public Role() {
}
public void setRoleId(int roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public ArrayList<Permission> getPermissions() {
return permissions;
}
public void setPermissions(ArrayList<Permission> permissions) {
this.permissions = permissions;
}
@Override
public String toString() {
return "Role{" +
"roleId=" + roleId +
", roleName='" + roleName + '\'' +
", permissions=" + permissions +
'}';
}
public Role(int roleId, String roleName, ArrayList<Permission> permissions) {
this.roleId = roleId;
this.roleName = roleName;
this.permissions = permissions;
}
}
mapper 接口mapper文件
只需要一个UserMapper
package com.wang.mapper;
import com.wang.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface UserMapper {
User queryUserByName(String userName);
}
UserMapper.xml
一个用户肯能有不同的角色,角色有不同的权限。使用多对多查询用户的所有角色和权限。
<?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">
<mapper namespace="com.wang.mapper.UserMapper">
<!-- 多对多查询-->
<select id="queryUserByName" resultMap="userResultMap">
select u.userId as uId,u.userName as uName,u.password as password ,
r.roleId as rId,r.roleName as roleName,
p.pId as pId,p.pName as pName
from user u, role r, user_role ur, role_permession rp , permission p
where u.userName = #{userName}
and u.userId = ur.userId
and ur.roleId = r.roleId
and r.roleId = rp.roleId
and rp.pId = p.pId
</select>
<resultMap id="userResultMap" type="user">
<result property="userId" column="uId"></result>
<result property="userName" column="uName"></result>
<result property="password" column="password"></result>
<collection property="roles" javaType="list" ofType="Role" >
<result property="roleName" column="roleName"></result>
<result column="roleId" property="roleName"></result>
<collection property="permissions" ofType="Permission" javaType="list">
<result property="pId" column="pId"></result>
<result property="pName" column="pName"></result>
</collection>
</collection>
</resultMap>
</mapper>
UserService &UserServiceImpl
package com.wang.service;
import com.wang.mapper.UserMapper;
import com.wang.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User queryUserByName(String userName) {
return userMapper.queryUserByName(userName);
}
}
package com.wang.service;
import com.wang.pojo.User;
public interface UserService {
User queryUserByName(String userName);
}
Realm 编写
两大核心:认证和授权
package com.wang.service;
import com.wang.pojo.Permission;
import com.wang.pojo.Role;
import com.wang.pojo.User;
import org.apache.shiro.authc.*;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
public class MyRealm extends AuthorizingRealm {
Logger log = LoggerFactory.getLogger(MyRealm.class);
@Autowired
private UserServiceImpl userService;
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("==>doGetAuthorizationInfo2222");
SimpleAuthorizationInfo simpleAuthorizationInfo =new SimpleAuthorizationInfo();
// 拿到当前登录对象 认证成功之后传递过来的
User userInfo = (User) principalCollection.getPrimaryPrincipal();
log.debug("测试日志------是否生效");
//从数据库获得角色和权限信息
for (Role role :userInfo.getRoles()) {
simpleAuthorizationInfo.addRole(role.getRoleName());
for (Permission permission : role.getPermissions()) {
simpleAuthorizationInfo.addStringPermission(permission.getpName());
}
}
return simpleAuthorizationInfo;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 1.从主体传过来的认证信息中,获得用户名
System.out.println("==>doGetAuthenticationInfo1111");
String userName = (String) authenticationToken.getPrincipal();
// 2.通过用户名到数据库中获取凭证
User user = userService.queryUserByName(userName);
// 如果数据库没有查询到则用户不存在
if (user == null) {
return null;
}
//同时密码验证也会做,密码错误则会抛出
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
user, // 用户名//给后序授权的地方传递
user.getPassword(), // 密码
null,//盐值
getName() // realm name
);
return simpleAuthenticationInfo;
}
}
ShiroConfig
过滤器注册
注入比要的安全组件 Realm DefaultWebSecurityManager ShiroFilterFactoryBean
package com.wang.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.wang.service.MyRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
//ShiroFilterBean
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager manager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(manager);
/*
*anon 无需认证
* authc 必须认证
* user 必须有记住我才能使用
* perms 拥有对某个资源的权限才能访问
* role 用户某个角色权限
*/
//拦截
Map<String ,String> filterMap = new LinkedHashMap<>();
filterMap.put("/user/*","anon");
filterMap.put("/blog/*","authc");
//拥有权限/user/** 才能访问/user/*
filterMap.put("/user/*","perms[/user/**]");
filterMap.put("/blog/*","perms[/blog/**]");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
shiroFilterFactoryBean.setLoginUrl("/");
return shiroFilterFactoryBean;
}
//DefaultWebSecurityManager
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("myRealm") MyRealm myRealm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(myRealm);
return defaultWebSecurityManager;
}
//创建realm 对象
@Bean
public MyRealm myRealm(){
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myRealm;
}
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
hashedCredentialsMatcher.setHashIterations(1);
return hashedCredentialsMatcher;
}
// 整合thymeleaf shiro
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
WebMvcConfig
视图定制
package com.wang.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//page url 指定视图名称,由于templates下面的静态资源不能直接访问
registry.addViewController("/main.html").setViewName("main");
registry.addViewController("/blog/blog.html").setViewName("/blog/blog");
registry.addViewController("/user/user.html").setViewName("/user/user");
}
}
controller
package com.wang.controller;
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.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class UserController {
// 前台登录功能实现
@RequestMapping("/login")
public String trsactionLogin(String username, String password, Model model){
// 获取登录用户对象
Subject subject = SecurityUtils.getSubject();
//登录令牌
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password);
try {
// 登录认证开始
subject.login(usernamePasswordToken);
//此处做了一个重定向
return "redirect:/main.html";
} catch (UnknownAccountException e) {
model.addAttribute("msg","用户名不存在");
return "index";
}catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码错误");
return "index";
}
}
}
前台页面
首页,登录功能
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>登录一下</h2>
<h4 th:text="${msg}" style="color: red"></h4>
<form th:action="@{/login}" method="post">
用户名:<input type="text" name="username">
密码:<input type="password" name = "password">
<input type="submit" value="登录" />
</form>
</body>
</html>
登录之后的主页
main.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
您好:
<a shiro:hasPermission="/blog/**" th:href="@{blog/blog.html}">博客管理</a>
<a shiro:hasPermission="/user/**" th:href="@{/user/user.html}">用户管理</a>
</body>
</html>
blog/blog.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2 >欢迎进入博客管理页面</h2>
</body>
</html>
user/user.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>欢迎进入用户管理页面</h2>
</body>
</html>
配置文件
application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/ssmbuild?useUnicode=true&serverTimezone=UTC&characterchaset=utf8
password: 1111
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.wang.pojo