springboot 数据库假面_基于springboot搭建的shiro框架--快速入门篇

本文档介绍了如何基于SpringBoot搭建Shiro框架,提供快速入门的步骤,包括配置依赖、数据库表创建、项目结构及代码编写。文章通过创建数据库表、配置文件、Controller等,展示了Shiro的登录验证和权限管理功能。
摘要由CSDN通过智能技术生成

最近因公司业务需要,研究了一下shiro框架,虽然在研究过程中也遇到过不少问题,不过好在最后都解决了。于是在这里写一篇文章记录一下自己的使用过程。本篇文章只讨论shiro的快速使用,暂不考虑原理分析,后续可能会出源码分析的文章。

话不多说,我们马上进入正题,本篇文章采用springboot2.0,jdk1.8。

先贴一张项目结构

一、引入相关依赖并添加配置

首先我们在搭建好的springboot框架中引入maven依赖。

org.apache.shiro

shiro-spring

1.4.0

当然除此之外还需要引入web相关依赖,如mysql,druid,mybatis等,同时我们在测试的过程中使用的时thymeleaf,相关的maven请自行添加。

org.springframework.boot

spring-boot-starter-thymeleaf

2.1.4.RELEASE

org.springframework.boot

spring-boot-starter-web

2.1.5.RELEASE

org.apache.commons

commons-lang3

RELEASE

com.alibaba

druid-spring-boot-starter

1.1.17

添加完成后我们在application中添加相关的配置,主要是druid,thymeleaf的相关配置。

server:

port: 8099

spring:

datasource:

type: com.alibaba.druid.pool.DruidDataSource

driverClassName: com.mysql.jdbc.Driver

platform: mysql

#注意修改成自己的仓库名称哦

url: jdbc:mysql://localhost:3306/candy?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8

username: root

password: root

# 连接池设置

druid:

initial-size: 5

min-idle: 5

max-active: 20

# 配置获取连接等待超时的时间

max-wait: 60000

# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒

time-between-eviction-runs-millis: 60000

# 配置一个连接在池中最小生存的时间,单位是毫秒

min-evictable-idle-time-millis: 300000

# Oracle请使用select 1 from dual

validation-query: SELECT 'x'

test-while-idle: true

test-on-borrow: false

test-on-return: false

# 打开PSCache,并且指定每个连接上PSCache的大小

pool-prepared-statements: true

max-pool-prepared-statement-per-connection-size: 20

# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙

filters: stat,wall,log4j2

# 通过connectProperties属性来打开mergeSql功能;慢SQL记录

connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

# 合并多个DruidDataSource的监控数据

use-global-data-source-stat: true

#thymeleaf模板引擎

thymeleaf:

mode: HTML5

encoding: utf-8

servlet:

content-type: text/html

#开发时关闭缓存

cache: false

suffix: .html

prefix: classpath:/templates/ #这里表示页面存放的位置是resource目录下的templates文件夹。

mybatis:

mapper-locations: classpath:mapper/*.xml #这里表示mapper的路径是resources文件下mapper文件夹下所有.xml结尾的文件,请修改成你自己的mapper存放的地址

configuration:

log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql、参数、查询结果

type-aliases-package: com.candy.shiro.entity #这里修改成你自己的实体类的路径

该配置文件中需要修改的地方基本上已经标注出来了,请注意修改,不要直接粘过去使用哦。

二、创建数据库表

本次项目一共需要5张表

其中比较重要的是用户表,角色表,权限表。具体得sql如下,直接粘贴运行即可

-- 权限表 --

create table permission (

pid int(11) not null auto_increment,

name varchar(255) not null default '',

url varchar(255) default '',

primary key(pid)

) engine = innoDB default charset = utf8;

INSERT INTO permission VALUES( '1','add','');

INSERT INTO permission VALUES( '2','delete','');

INSERT INTO permission VALUES( '3','edit','');

INSERT INTO permission VALUES( '4','query','');

-- 用户表 --

create table user(

uid int(11) not null auto_increment,

username varchar(255) not null default '',

password varchar(255) not null default '',

primary key(uid)

) engine = InnoDB default charset = utf8;

INSERT INTO user VALUES( '1','admin','123');

INSERT INTO user VALUES( '2','demo','123');

-- 角色表 --

create table role(

rid int(11) not null auto_increment,

rname varchar(255) not null default '',

primary key(rid)

) engine = InnoDB default charset = utf8;

INSERT INTO ROLE VALUES( '1','admin');

INSERT INTO ROLE VALUES( '2','customer');

-- 权限角色关系表 --

create table permission_role(

rid int(11) not null,

pid int(11) not null,

key idx_rid (rid),

key id_pid (pid)

) engine = InnoDB default charset = utf8;

insert INTO permission_role VALUES('1','1');

insert INTO permission_role VALUES('1','2');

insert INTO permission_role VALUES('1','4');

insert INTO permission_role VALUES('1','3');

insert INTO permission_role VALUES('2','1');

insert INTO permission_role VALUES('2','4');

-- 用户角色关系表 --

CREATE TABLE user_role(

uid int(11) not null,

rid int(11) not null,

key idx_uid (uid),

key idx_rid (rid)

)ENGINE = INNODB default CHARSET = utf8;

INSERT INTO user_role VALUES(1,1);

INSERT INTO user_role VALUES(2,2);

三、生成项目结构,编写sql

这里我直接使用easy code直接生成了项目结构,非常好用,推荐大家使用。当然你也可以使用其它得代码自动生成工具,如果你得项目结构已经生成了,请继续向下看。

我们找到刚才生成的User实体类,在里面加上

private Setroles = new HashSet<>();

并生成getter setter方法;同时在Role实体类中添加

private Setpermissions = new HashSet<>();

private Setusers = new HashSet<>();

并生成getter setter 方法

添加完成之后我们在UserDao中添加一个方法

/**

* @Author : qzx

* @Description : //TODO 通过名字查询用户

* @Date : 16:34 2019/6/13

* @Param : [username]

* @return : com.candy.shiro.entity.User

**/

User findByUserName(@Param("username") String username);

通过用户名查找对应的用户,添加完成后再UserMapper.xml中编写对应的sql

新建一个 resultMap

使我们在查询User的同时也会查到该User的角色和该角色拥有的权限。

接下来编写sql语句:

select u.*,r.*,p.*

from user u

inner join user_role ur on ur.uid = u.uid

inner join role r on r.rid = ur.rid

INNER JOIN permission_role pr on pr.rid = r.rid

INNER JOIN permission p on p.pid = pr.pid

where u.username = #{username}

连接查询出用户的角色和权限。

写完了这些别忘了在service层中调用这个方法哦。

这样我们就做完了前期的准备工作了,接下来开始进入重点!!!

四、编写shiro配置文件

简单的实现shiro做登录验证,我们只需要编写两个配置类,AuthRealm配置类和ShiroConfiguration配置类,其中AuthRealm的主要目的使从前端接收我们的登录用户的参数,并将参数发送给shiro框架的SimpleAuthorizationInfo类进行验证。ShiroConfiguration配置类主要是为了定义角色或权限可以访问的路径,以及一些基本的配置。

首先我们编写AuthRealm类

package com.candy.shiro.conf.shiro;

import com.candy.shiro.entity.Permission;

import com.candy.shiro.entity.Role;

import com.candy.shiro.entity.User;

import com.candy.shiro.service.UserService;

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.apache.shiro.util.CollectionUtils;

import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;

import java.util.Set;

/**

* @author :q'z'x

* @ClassName :AuthRealm

* @date : 2019/6/13 16:22

* @description : TODO

*/

public class AuthRealm extends AuthorizingRealm {

@Autowired

UserService userService;

//授权

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

/**

* @Author : qzx

* @Description : //TODO 从session里面获取用户,并根据该用户查询所在的角色和该角色的权限

* @Date : 16:54 2019/6/13

* @Param : [principalCollection]

* @return : org.apache.shiro.authz.AuthorizationInfo

**/

User user = (User) principalCollection.fromRealm(this.getClass().getName()).iterator().next();

ArrayListpermissionList = new ArrayList<>();

ArrayListroleNameList = new ArrayList<>();

SetroleSet = user.getRoles();

//遍历该用户的角色和权限,并存放到list中

if(!CollectionUtils.isEmpty(roleSet)){

for (Role role : roleSet){

roleNameList.add(role.getRname());

SetpermissionSet = role.getPermissions();

if(!CollectionUtils.isEmpty(permissionSet)){

for(Permission permission : permissionSet){

permissionList.add(permission.getName());

}

}

}

}

//将角色和权限交给shiro处理

SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

info.addStringPermissions(permissionList);

info.addRoles(roleNameList);

return info;

}

//认证登录

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;

String username = usernamePasswordToken.getUsername();

//这个接口改成自己的接口

User user = userService.findByUserName(username);

return new SimpleAuthenticationInfo(user,user.getPassword(),this.getClass().getName());

}

}

这个配置类里有登录验证和授权验证。登录认证主要是把该用户的所有信息都查询到,主要还是看授权认证。授权认证需要把用户从session中获取,然后将用户的角色和该角色的权限全部遍历出来,交给shrio处理。

另一个配置类代码如下:

package com.candy.shiro.conf.shiro;

import org.apache.shiro.cache.MemoryConstrainedCacheManager;

import org.apache.shiro.mgt.SecurityManager;

import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;

import org.apache.shiro.web.mgt.DefaultWebSecurityManager;

import org.apache.shiro.web.servlet.SimpleCookie;

import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;

/**

* @author :qzx

* @ClassName :ShiroConfiguration

* @date : 2019/6/13 17:32

* @description : TODO

*/

@Configuration

public class ShiroConfiguration {

@Bean("shiroFilter")

public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") SecurityManager manager) {

ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();

bean.setSecurityManager(manager);

//登录

bean.setLoginUrl("/login");

//登录成功之后跳转

bean.setSuccessUrl("/index");

//没有请求访问的时候

bean.setUnauthorizedUrl("/unauthorized");

//以下是某些请求如何拦截

LinkedHashMapfilterChainDefinitionMap = new LinkedHashMap<>();

//DefaultFilter类里有关于authc,anon的相关信息,这是shiro默认的几个验证方法

filterChainDefinitionMap.put("/index","authc");

filterChainDefinitionMap.put("/login","anon");

filterChainDefinitionMap.put("/loginUser","anon");

//只允许角色为admin的用户访问

filterChainDefinitionMap.put("/admin","roles[admin]");

//表示具有edit的权限才可以访问

filterChainDefinitionMap.put("/edit","perms[edit]");

//这个必须放在最后面表示其它接口是不需要登录认证的

filterChainDefinitionMap.put("/**","user");

bean.setFilterChainDefinitionMap(filterChainDefinitionMap);

return bean;

}

@Bean("securityManager")

public DefaultWebSecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm){

DefaultWebSecurityManager manager = new DefaultWebSecurityManager();

manager.setRealm(authRealm);

return manager;

}

@Bean("authRealm")

public AuthRealm authRealm(@Qualifier("credentialMatcher") CredentialMatcher matcher){

AuthRealm authRealm = new AuthRealm();

authRealm.setCredentialsMatcher(matcher);

//开启缓存

authRealm.setCacheManager(new MemoryConstrainedCacheManager());

return authRealm;

}

@Bean("credentialMatcher")

public CredentialMatcher credentialMatcher(){

return new CredentialMatcher();

}

/**

* rememberMe cookie 效果是重开浏览器后无需重新登录

*

* @return SimpleCookie

*/

private SimpleCookie rememberMeCookie() {

// 设置 cookie 名称,对应 login.html 页面的

SimpleCookie cookie = new SimpleCookie("rememberMe");

// cookie.setSecure(true); // 只在 https中有效 注释掉 正常

// 设置 cookie 的过期时间,单位为秒,这里为一天

cookie.setMaxAge(2000);

return cookie;

}

//shiro和Spring相关联

@Bean

public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager manager){

AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();

authorizationAttributeSourceAdvisor.setSecurityManager(manager);

return authorizationAttributeSourceAdvisor;

}

@Bean

public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){

DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();

creator.setProxyTargetClass(true);

return creator;

}

}

shiroFilterFactoryBean是里面最重要的一个类了,每一步的意思我都已经加注释了,比较关键的就setFilterChainDefinitionMap里面的配置了,比如说filterChainDefinitionMap.put("/index",“authc”); 这句代码表示想要访问index这个路径的页面,则必须经过表单验证通过才可以。filterChainDefinitionMap.put("/admin",“roles[admin]”);表示只有角色admin才可以访问/admin这个路径。具体的可以进到DefaultFilter类里查看实现。

另外要注意的是filterChainDefinitionMap.put("/**",“user”);这行代码必须放在最后,不然会出问题的。

到这里为止我们的准备工作就差不多了,下面我们要写controller类了。

五、编写controller类和页面并进行测试

package com.candy.shiro.controller;

import com.candy.shiro.entity.User;

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.UsernamePasswordToken;

import org.apache.shiro.subject.Subject;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

/**

* @author :qzx

* @ClassName :TestController

* @date : 2019/6/13 18:07

* @description : TODO

*/

@Controller

public class TestController {

@RequestMapping("/login")

public String login() {

return "login";

}

@RequestMapping("/index")

public String index() {

return "index";

}

@RequestMapping("/unauthorized")

public String unauthorized(){

return "unauthorized";

}

@RequestMapping("/admin")

@ResponseBody

public String admin() {

return "admin success";

}

@RequestMapping("/edit")

@ResponseBody

public String edit() {

return "edit success";

}

@RequestMapping("/logout")

public String logout() {

//取出当前验证的主体

Subject subject = SecurityUtils.getSubject();

if(subject != null){

//退出登录

subject.logout();

}

return "login";

}

@RequestMapping("/loginUser")

public String loginUser(@RequestParam("username") String username, @RequestParam("password") String password, HttpSession session) {

UsernamePasswordToken token = new UsernamePasswordToken(username, password);

Subject subject = SecurityUtils.getSubject();

//shiro的认证逻辑

try {

subject.login(token);

User user = (User) subject.getPrincipal();

session.setAttribute("user", user);

return "index/index";

} catch (Exception e) {

return "login";

}

}

}

controller类的代码很好理解,基本上不用解释,注意自己的路径,有可能和我的不一样。

接下来我们在templates下面写页面,页面结构参考我最开始发的那张结构图(如果想和我的路径一样的话)。

login的页面:

Login

登录

index的页面:

Login

欢迎,您已登录成功!

到这里我们就可以启动一下进行测试了,启动项目,打开浏览器输入 http://localhost:8099/login 进入到登录页面(如果没有进行登录的话,访问其它接口会自动跳转到登录页面)。

输入账号密码(admin;123)会进入登录成功界面:

这是我们修改地址后缀为edit,点击访问,会访问到以下页面,说明访问成功了,admin拥有访问edit接口的权限。

接下来我们将后缀改为logout退出登录,重新输入账号密码(demo;123)点击访问登录成功后,将后缀改为edit,会出现如下假面

系统自动跳转到unauthorized接口,说明该用户没有权限访问edit(可以自己定义一个无权限访问的页面)。

六、结语

到这里为止,我们的shiro使用方法就都已经介绍完毕了,不知道小伙伴有没有学会呢?如果学会了使用方法,建议自己打断点走一走内部实现流程,通过源码分析来进一步加深对shiro框架的理解和操作。喜欢本篇文章或者有什么想问的欢迎在评论区留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值