前言:
学习的地址:https://www.majiaxueyuan.com/front/showcoulist 、https://www.bilibili.com/video/av44084437
SpringBoot的pom依赖(以2.0版本为例的)
本集记录的是shiro的权限框架(记录的是一个比较偏向实战的demo)
使用了springboot + freemark + shiro+mybatis 这样的框架
注意:有些代码是在上一章 https://blog.csdn.net/qq_28198181/article/details/97929008 上的
demo层次:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL4dGVNRzaU1ENJpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL1IDOxUjMzYTMxADOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
大体流程:
从页面得到登录密码和用户,进入数据库查询,如果有就能进入主页,如果不能,就返回登录页面。登录到主页之后,根据用户的角色显示不同的权限进行不同的操作。
这里面还需要处理realm的授权操作。就是对用户的权限进行判断。
目录
1.添加依赖
2.配置application.properties文件
3.创建数据库表和和字段
4.创建实体类和mapper接口
5.修改realm的认证和授权方法
6.修改前端显示方法。
7.测试
1.添加依赖
在上次之前的demo依赖上还需要添加前端支持shiro的依赖和连接数据库的依赖,所有的依赖如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入freeMarker的依赖包. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<!-- 前端支持shiro的标签-->
<dependency>
<groupId>net.mingsoft</groupId>
<artifactId>shiro-freemarker-tags</artifactId>
<version>0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- atomikos 用于处理多数数据源配置的事务管理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
暂时使用到的依赖就这么多。
2.配置application.properties文件
application.properties在上一次的基础上还需要添加连接数据库的配置
server.port=8080
server.tomcat.uri-encoding=UTF-8
spring.freemarker.allow-request-override=false
spring.freemarker.cache=true
spring.freemarker.charset=UTF-8
spring.freemarker.check-template-location=true
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=false
spring.freemarker.expose-session-attributes=false
spring.freemarker.expose-spring-macro-helpers=false
spring.freemarker.suffix=.ftl
spring.freemarker.template-loader-path=classpath:/templates/
mysql.datasource.data.borrowConnectionTimeout=30
mysql.datasource.data.loginTimeout=30
mysql.datasource.data.maintenanceInterval=60
mysql.datasource.data.maxIdleTime=60
mysql.datasource.data.maxLifetime=20000
mysql.datasource.data.maxPoolSize=25
mysql.datasource.data.minPoolSize=3
mysql.datasource.data.password=jy897513;
mysql.datasource.data.url=jdbc:mysql://localhost:3306/bootdb2?useUnicode=true&characterEncoding=utf-8
mysql.datasource.data.username=root
自己根据自己数据库去配置连接地址
暂时使用的如上。
3.创建数据库表和和字段
数据库中涉及到的表如下:
1.用户表user
用户表是记录登录的用户名称和密码,简单点就只是创建了三个字段 uid uname 和 upassword
CREATE TABLE user(
uid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
uname VARCHAR(20) NOT NULL,
upassword VARCHAR(20) NOT NULL
);
2.角色表role
角色表是判断用户的在当前系统中是什么角色,是普通user用户还是admin管理用户
也只是设置3个字段 rid rname 和 rdescription
CREATE TABLE role(
rid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
rname VARCHAR(20),
rdescription VARCHAR(100)
);
3.权限表permission
权限表只是自己加的,用来判断什么角色拥有什么权限,根据权限来显示可执行的操作
权限也只是写了三个参数 pid pname pdescription
CREATE TABLE permission(
pid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
pname VARCHAR(20),
pdescription VARCHAR(100)
);
4.用户表和角色表的关联表
记录用户id和角色id的关联表 可以根据用户id查询到角色
也只是设置了三个字段 urid uid 和rid
CREATE TABLE role_user(
urid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
uid INT NOT NULL,
rid INT NOT NULL
);
目的就是为了得到用户后可以去知道他是什么角色 为后面得到权限做准备
5.角色表和权限表的关联表
设置了三个字段 rpid rid 和pid
CREATE TABLE role_permission(
rpid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
rid INT NOT NULL,
pid INT NOT NULL
);
这样的所以用到的数据库表就创建好了
4.创建实体类和mapper接口
实体类创建是为了能从数据库中取得数据组成PO,
具体有三个实体类,分别就是role user 和permission
user:
@Data
public class User {
private Integer uid;
private String uname;
private String upassword;
}
role:
@Data
public class Role {
private Integer rid;
private String rname;
private String rdescription;
}
permission:
@Data
public class Permission {
private Integer pid;
private String pname;
private String pdescription;
}
实体类创建好后需要配置数据源(我就直接用多数据源配置了)
dbconfig类:
@Data
@ConfigurationProperties(prefix = "mysql.datasource.data")
public class DBConfig {
private String url;
private String username;
private String password;
private int minPoolSize;
private int maxPoolSize;
private int maxLifetime;
private int borrowConnectionTimeout;
private int loginTimeout;
private int maintenanceInterval;
private int maxIdleTime;
private String testQuery;
}
扫描配置文件前缀为 mysql.datasource.data开头的参数。
mybatisConfigure类
@Configuration
@MapperScan(basePackages = "com.zqj.mapper",sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class MyBatisPrimaryConfig {
@Bean(name = "primaryDataSource")
@Primary
public DataSource primaryDataSource(DBConfig dbConfig) throws SQLException {
//配置数据库参数
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(dbConfig.getUrl());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setPassword(dbConfig.getPassword());
mysqlXaDataSource.setUser(dbConfig.getUsername());
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("primaryDataSource");
xaDataSource.setMinPoolSize(dbConfig.getMinPoolSize());
xaDataSource.setMaxPoolSize(dbConfig.getMaxPoolSize());
xaDataSource.setMaxLifetime(dbConfig.getMaxLifetime());
xaDataSource.setBorrowConnectionTimeout(dbConfig.getBorrowConnectionTimeout());
xaDataSource.setLoginTimeout(dbConfig.getLoginTimeout());
xaDataSource.setMaintenanceInterval(dbConfig.getMaintenanceInterval());
xaDataSource.setMaxIdleTime(dbConfig.getMaxIdleTime());
xaDataSource.setTestQuery(dbConfig.getTestQuery());
return xaDataSource;
}
@Primary
@Bean(name = "pariamrySqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Primary
@Bean(name = "primarySqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("pariamrySqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
配置的datasource扫描的mapper包在com.zqj.mapper
这个时候就是需要去编写mapper接口了
创建mapper包 然后我创建了三个mapper
usermapper 用来查询用户的
@Mapper
@Repository
public interface UserMapper {
@Select("SELECT * FROM user WHERE uname LIKE #{username} limit 1;")
public User getUser(@Param("username") String username);
}
RoleUserMapper
是用来根据用户查询对应绑定的角色
@Mapper
@Repository
public interface RoleUserMapper {
@Select("SELECT r.rid, r.rname,r.rdescription FROM user u,role r,role_user ru WHERE u.uname =#{userName} and u.uid = ru.uid and ru.rid = r.rid;")
List<Role> getRoleList(@Param("userName") String userName);
}
RolePermissionMapper
是根据用户角色来查询绑定的权限的
@Mapper
@Repository
public interface RolePermissionMapper {
@Select("SELECT p.pid,p.pname FROM permission p , role_permission pr ,role r WHERE r.rname = #{roleName} and r.rid =pr.rid and p.pid=pr.pid;")
List<Permission> getPermissions(@Param("roleName") String roleName);
}
mapper写好后,就可以连接数据库了
5.修改realm的认证和授权方法
mapper 是实体都创建好了,可以去修改realm的认证和授权了。
现将以前写死的认证方法的password改了:
@Autowired
private UserMapper userMapper;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = (String) authenticationToken.getPrincipal(); //传递过来的username 就是登陆的用户名称
// String dbpassWord = "123"; //这里模拟的是数据库的用户密码
User user = userMapper.getUser(username);
System.out.println("进入认证Realm:" + user.toString());
//这里是处理某处登陆后,当前登录失效的方法,就像异地登录那样
Collection<Session> sessions = sessionDAO.getActiveSessions();
for (Session session : sessions) {
String loginedUserName = String.valueOf(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY));
if (username.equals(loginedUserName)) {
//说明已经是登陆成功的了。需要挤掉
session.setTimeout(0L);
break;
}
}
SimpleAuthenticationInfo simpleAuthorizationInfo = null;
//这里是将username 和 数据库得到的密码放进去 最后一个参数可以写死
//实战是在数据库查到数据后将这个数据放进去
//后期还可以加盐
simpleAuthorizationInfo = new SimpleAuthenticationInfo(username, user.getUpassword(), "realName");
return simpleAuthorizationInfo;
}
认证修改后,如果用户正常登录的话,就可以去处理授权的问题了。
处理授权是需要首先在前端获得shiro传递过来的参数:
前台shiro tags和后台的联动:这个是处理shiro的标签
@Component
public class FreemarkShiroConfig {
@Autowired
private FreeMarkerConfigurer configurer;
//设置shrio和freemark联动的标签
@PostConstruct
public void setShiroTags() {
configurer.getConfiguration().setSharedVariable("shiro",new ShiroTags());
}
}
当前后台联动处理好了后就会根据shiro进入到authorizationInfo的doGetAuthrizationInfo方法
授权主要是在登陆之后进行用户角色判断和权限判断:将得到的用户名查询到对应的角色和权限
@Autowired
private RolePermissionMapper rolePermissionMapper;
/**
* 授权
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//得到当前用户名称
String username = (String) principalCollection.getPrimaryPrincipal();
System.out.println("进入到授权Realm:" + username);
//得到从数据库取出的用户的角色对象列表和全县列表
List<Role> dbRoleList = roleUserMapper.getRoleList(username);
List<Permission> dbPermissionList = new ArrayList<>();
//要添加的角色列表
List<String> roleList = new ArrayList<>();
//要添加的权限列表
List<String> permissionList = new LinkedList<>();
//得到数据库的角色列表
for (Role role : dbRoleList) {
//添加角色名称
roleList.add(role.getRname());
//权限列表
//得到角色拥有的权限
dbPermissionList = rolePermissionMapper.getPermissions(role.getRname());
for (Permission permission : dbPermissionList) {
if (permission.getPname().equals("SELECT")) {
permissionList.add("ADMIN:USER:"+permission.getPname());
}else{
permissionList.add(role.getRname()+":"+permission.getPname());
}
}
}
//把角色列表和权限列表发送过来
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//这里是将得到的角色列表和权限列表放到授权对象中
simpleAuthorizationInfo.addRoles(roleList);
simpleAuthorizationInfo.addStringPermissions(permissionList);
return simpleAuthorizationInfo;
}
接下来可以去处理前端显示了
6.修改前端显示方法
将授权写好后,可以将其体现到前端去
index页面是显示得到的角色信息和权限信息
index页面使用shiro标签时会找到联动的@requiresRole 和 @RequiresPermission
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head >
<meta charset="UTF-8"/>
<title></title>
</head>
<body>
你好啊:${name}
</br>
</br>
你当前的角色是:
<@shiro.hasRole name="ADMIN"> <!--对应的是 @requiresRole -->
<p>ADMIN角色</p>
</@shiro.hasRole>
<@shiro.hasRole name="USER">
<p>USER角色</p>
</@shiro.hasRole>
<@shiro.hasRole name="MANAGER">
<p>MANAGER角色</p>
</@shiro.hasRole>
你当前可以操作的功能有:
<@shiro.hasPermission name="ADMIN:UPDATE"><!--对应的是 @requiresPermission -->
<p>UPDATE功能</p>
</@shiro.hasPermission>
<@shiro.hasPermission name="ADMIN:DELETE">
<p>DELETE功能</p>
</@shiro.hasPermission>
<@shiro.hasPermission name="ADMIN:INSERT">
<p>INSERT功能</p>
</@shiro.hasPermission>
<@shiro.hasPermission name="ADMIN:USER:SELECT">
<p>SELECT功能</p>
</@shiro.hasPermission>
退出:
<form action="toLogout" method="get">
<input type="submit" name="logout" value="退出"/><br/>
</form>
</body>
</html>
上面的@shiro.xxxxx的就是要去userRealm的授权中查找得到的角色数据和权限数据
name后面跟的是 用户角色:权限 这样的表达方式
现在数据库中还没有 等下写进去
最后将dbconfig.class进行在程序执行的时候去加载:
@SpringBootApplication
@EnableConfigurationProperties({DBConfig.class})
public class ShiroApplication {
public static void main(String[] args) {
SpringApplication.run(ShiroApplication.class, args);
}
}
7.测试
大体流程写完了,现在在数据库中放些数据
用户表:
角色表:
权限表:
用户角色关联表:
表示的是我姬子阿姐有管理员权限和普通用户权限
神舟平板只有普通用户权限
然后将权限表和角色关联表关联起来:
ADMIN用户拥有增删改查所有功能
USER用户拥有查询功能
后台数据库也模拟好了,
http://localhost:8080/index/getIndex
这个时候可以操作一波试试了
访问indexContrller
访问index界面
会跳转回到login界面 ,因为没有认证通过的
接着
我们来输入用户密码 随便输一个,会报错输入错误
现在我们输入一个正确的数据来尝试一下
以上。
没有很严谨的去写。可能有些规范不好。请指正