​Shiro​​安全框架的讲解和使用!​​Shiro​​是​​Apache​​下的一个开源项目,我们称之为​​Apache Shiro​​。它是一个很易用与​​Java​​项目的的安全框架,提供了认证、授权、加密、会话管理,与​​spring Security​​ 一样都是做一个权限的安全框架,但是与​​Spring Security ​​相比,在于 ​​Shiro​​ 使用了比较简单易懂易于使用的授权方式。​​shiro​​属于轻量级框架,相对于​​security​​简单的多,也没有​​security​​那么复杂。

开篇:​​Shiro​​安全框架的介绍

Shiro安全框架_数据


Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份;

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;

Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

Web Support:Web 支持,可以非常容易的集成到 Web 环境;

Caching:缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率;

Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

Testing:提供测试支持;

Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。


记住一点,​​Shiro ​​​不会去维护用户、维护权限;这些需要我们自己去设计 / 提供;然后通过相应的接口注入给 ​​Shiro ​​即可。

从外部来看 ​​Shiro ​​,如下图。

Shiro安全框架_数据_02


Subject:主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;

SecurityManager:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;

Realm:域,Shiro 从从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。


一,​​Shiro​​认证

首先新建一个​​springboot​​​项目,在​​maven​​​中加入​​shiro-core​​​核心包和​​junit​​测试包。

<!--加入shiro核心包-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.3</version>
</dependency>
<!--junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>

然后新建一个类,进行​​Shiro认证​​的测试。

步骤说明


1,构建securityManager环境

2,创建Realm,设置账号和密码

3,把设置好的Realm添加到securityManager环境

4,主体提交认证请求

5,创建UsernamePasswordToken,相当于登录操作,获取登录时的账号和密码

6,subject.isAuthenticated()判断账号和密码是否正确


public class ShiroController {
//创建Realm是对用户的账号和密码进行管理的
SimpleAccountRealm simpleAccountRealm=new SimpleAccountRealm();
@Before //都是junit的注解,目的是在单元测试中可以调用本方法
public void addUser(){
//这里设置好用户名和密码
simpleAccountRealm.addAccount("admin","123");
}
@Test
public void testAuthentication(){
//1,构建securityManager环境
DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
//把上面的用户名密码设置添加到securityManager环境
defaultSecurityManager.setRealm(simpleAccountRealm);
//2,主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//这里是登录环境,获取到账号和密码,shiro会自动识别是否正确
UsernamePasswordToken token=new UsernamePasswordToken("admin","123");
//测试登录是否可以成功
subject.login(token);
boolean authenticated = subject.isAuthenticated();//true验证通过,false账号密码不正确
System.err.println("是否可以登录"+authenticated); //true
//shiro的登出方法
subject.logout();
boolean authenticated1 = subject.isAuthenticated();
System.out.println("已经退出了,状态为false ########"+authenticated1); //false
}
}

二,​​Shiro​​授权

在​​simpleAccountRealm.addAccount​​的操作里可以添加对应的权限


比如说,给admin账户添加一个admin权限(可以设置多个权限)


SimpleAccountRealm simpleAccountRealm=new SimpleAccountRealm();
@Before //都是junit的注解,目的是在单元测试中可以调用本方法
public void addUser(){
//这里设置好用户名和密码
simpleAccountRealm.addAccount("admin","123","admin");
}


然后再下面登录验证通过之后,再验证是否具有该权限


subject.checkRoles("admin");

如果该账户有​​admin​​权限则顺利通过,没有则会报错。

Shiro安全框架_数据_03

三,​​Shiro​​​自定义​​Realm​

在说自定义​​Realm​​​之前,先说一下​​Shiro​​​的内置​​Realm​​。


  • IniRealm
  • JdbcRealm

1,​​IniRealm​​的使用

用户是由​​IniRealm​​控制的。

创建一个​​IniRealm​​​,指定账号密码的文件(​​resources​​​文件夹下的​​user.ini​​文件)

IniRealm iniRealm=new IniRealm("classpath:user.ini");

然后把该​​Realm​​​添加到​​securityManager​​环境

//把上面的用户名密码设置添加到securityManager环境
defaultSecurityManager.setRealm(iniRealm);

在​​resources​​​文件夹下新建一个文件​​user.ini​​用来存储用户信息和权限。

​users​​​下面是账号密码,“=”左边是账号,“=”右边是密码,密码后边是对应的权限,可以设置多个。​​roles​​下面是对应权限下的操作的权限。

[users]
Mark=123456,admin
[roles]
admin=delete

权限验证

//验证该用户是否有admin权限
subject.checkRoles("admin");
//验证该用户是否具有admin权限下的delete操作权限
//subject.checkPermission("delete"); //可以
subject.checkPermission("add"); //不可以

该类完整实例

public class IniRealmTest {
//创建一个IniRealm,指定账号密码的文件(resources文件夹下的user.ini文件)
IniRealm iniRealm=new IniRealm("classpath:user.ini");
@Test
public void testAuthentication(){
//1,构建securityManager环境
DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
//把上面的用户名密码设置添加到securityManager环境
defaultSecurityManager.setRealm(iniRealm);
//2,主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//这里是登录环境,获取到账号和密码,shiro会自动识别是否正确
UsernamePasswordToken token=new UsernamePasswordToken("Mark","123456");
//测试登录是否可以成功
subject.login(token);
boolean authenticated = subject.isAuthenticated();//true验证通过,false账号密码不正确
System.err.println("是否可以登录"+authenticated);
//验证该用户是否有admin权限
subject.checkRoles("admin");
//验证该用户是否具有admin权限下的delete操作权限
// subject.checkPermission("delete"); //可以
subject.checkPermission("add"); //不可以
}
}

2,​​JdbcRealm​​的使用

首先需要加入​​Druid​​​包和​​mysql​​驱动包。

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>

它是通过查询数据库表来进行操作的。

public class JdbcRealmTest {
DruidDataSource dataSource=new DruidDataSource();
{
dataSource.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("1234");
}
@Test
public void testAuthentication(){
JdbcRealm jdbcRealm=new JdbcRealm();
jdbcRealm.setDataSource(dataSource);
//开启数据库授权,默认为关闭false
jdbcRealm.setPermissionsLookupEnabled(true);
//从数据库中test_user表中查找用户
String sql="select password from test_user where account=?";
jdbcRealm.setAuthenticationQuery(sql);
//从数据库中test_user_role表中查询权限
String roleSql="select user_role from test_user_role where user_name=?";
jdbcRealm.setUserRolesQuery(roleSql);
//1,构建securityManager环境
DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
//把上面的用户名密码设置添加到securityManager环境
defaultSecurityManager.setRealm(jdbcRealm);
//2,主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//这里是登录环境,获取到账号和密码,shiro会自动识别是否正确
UsernamePasswordToken token=new UsernamePasswordToken("admin","123");
//测试登录是否可以成功
subject.login(token);
boolean authenticated = subject.isAuthenticated();//true验证通过,false账号密码不正确
System.err.println("是否可以登录"+authenticated);
subject.checkRole("管理员权限");
}
}

数据库表

​test_user​​表(用户表)

Shiro安全框架_数据库_04

​test_user_role​​表(用户对应权限表)

Shiro安全框架_数据_05

打开​​JdbcRealm​​​类查看源码,其实它已经默认封装了一些​​sql​​​,如果不是自定义的话,可以默认使用它提供的​​sql​​​,这样就可以不用手动写​​sql​​进行查询了。

Shiro安全框架_数据库_06

3,自定义​​Realm​

新建一个类,继承​​AuthorizingRealm​​类。


doGetAuthorizationInfo方法是授权方法。

步骤:

1,从数据库中或缓存中获取角色权限数据

2,获取操作权限数据

3,创建SimpleAuthorizationInfo并设置数据并返回

doGetAuthenticationInfo方法是认证方法

步骤:

1,从主体传过来的认证信息中,获得用户名

2,通过用户名到数据库中获取凭证

3,创建SimpleAuthenticationInfo返回对象(参数:用户名,密码,自定义Realm的名字)


public class CustomRealm extends AuthorizingRealm {
//做授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username= (String) principals.getPrimaryPrincipal();
//1,从数据库中或缓存中获取角色权限数据
Set<String> roles=getRolesByUsername(username);
//再获取操作权限数据
Set<String> permissions=getPermissionsByUsername(username);
SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setStringPermissions(permissions);
simpleAuthorizationInfo.setRoles(roles);
return simpleAuthorizationInfo;
}
//模拟数据库根据用户名取出操作权限数据
private Set<String> getPermissionsByUsername(String username) {
Set<String> set=new HashSet<>();
set.add("delete");
return set;
}
//模拟数据库根据用户名取出权限数据
private Set<String> getRolesByUsername(String username) {
Set<String> set=new HashSet<>();
set.add("管理员权限");
return set;
}
//做认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//1,从主体传过来的认证信息中,获得用户名
String username= (String) token.getPrincipal();
//2,通过用户名到数据库中获取凭证
String password=getPasswordByUsername(username);
//创建SimpleAuthenticationInfo返回对象(参数:用户名,密码,自定义Realm的名字)
SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(username,password,"customRealm");
return authenticationInfo;
}
//模拟数据库通过username拿password
private String getPasswordByUsername(String username) {
String password="123456";
return password;
}
}

写好自定义​​Realm​​之后,然后进行测试

还是上面的内容

直接把​​Realm​​​添加到​​securityManager​​环境,运行可以通过。如果账号密码错误,或者没有对应权限,则报错。

@Test
public void testAuthentication(){
CustomRealm customRealm=new CustomRealm();
//1,构建securityManager环境
DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
//把上面的用户名密码设置添加到securityManager环境
defaultSecurityManager.setRealm(customRealm);
//2,主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//这里是登录环境,获取到账号和密码,shiro会自动识别是否正确
UsernamePasswordToken token=new UsernamePasswordToken("Mark","123456");
//测试登录是否可以成功
subject.login(token);
boolean authenticated = subject.isAuthenticated();//true验证通过,false账号密码不正确
System.err.println("是否可以登录"+authenticated);
//测试是否有对应权限
subject.checkRoles("管理员权限");
subject.checkPermission("delete");
}

4,​​Shiro​​加密

在上面自定义​​Realm​​的基础上进行加密。

首先把​​Realm​​​类中的密码设置成​​Md5​​加密后的密文。

//模拟数据库通过username拿password
private String getPasswordByUsername(String username) {
Md5Hash md5Hash=new Md5Hash("123456");
return md5Hash.toString();
}

然后把​​shiro​​​加密设置到​​Realm​​中。

//shiro加密
HashedCredentialsMatcher hashedCredentialsMatcher=new HashedCredentialsMatcher();
//设置加密算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//设置加密次数
hashedCredentialsMatcher.setHashIterations(1);
//把shiro加密设置到Realm中
customRealm.setCredentialsMatcher(hashedCredentialsMatcher);

这样做的目的是因为数据库中的密码是加密后的密文,那么​​Shiro​​进行密码校验是否正确时,会和密文相比较是否正确。

运行结果当然为​​true​​。

四,​​springboot​​​整合​​shiro​​安全框架


shiro有三个核心内容


1,导入​​springboot​​​集成​​shiro​​依赖

首先添加​​shiro​​​整合​​spring​​​的依赖包,页面跳转用到了​​thymeleaf​​,自己导依赖包。

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>

2,创建一个自定义​​Realm​

public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
return null;
}

3,创建​​ShiroConfig​​类


在ShiroConfig类一共做三步操作:

第一步,创建Realm对象 ----连接数据
第二步,创建DefaultWebSecurityManager环境 ----关联Realm
第三步,创建ShiroFilterFactoryBean ----将第二部的环境设置进去


@Configuration
public class ShiroConfig{
/**
*
* Subject 用户
* SecurityManager 管理所有用户
* Realm 连接数据
*
* 第一步,创建Realm对象 ----连接数据
* 第二步,创建DefaultWebSecurityManager环境 ----关联Realm
* 第三步,创建ShiroFilterFactoryBean ----将第二部的环境设置进去
*/
//创建Realm对象,自定义类
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
// @Bean(name = "securityManager")是把该对象加到spring容器
@Bean(name = "securityManager")
public DefaultSecurityManager getDefaultSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultSecurityManager securityManager=new DefaultWebSecurityManager();
//关联Realm对象
securityManager.setRealm(customRealm);
return securityManager;
}
//@Qualifier("securityManager")是根据名字从spring容器中拿到该对象
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultSecurityManager defaultSecurityManager){
ShiroFilterFactoryBean factoryBean=new ShiroFilterFactoryBean();
//设置安全管理器
factoryBean.setSecurityManager(defaultSecurityManager);
return factoryBean;
}
}

4,创建​​test​​页面

三个​​html​​页面

首页

<body>
欢迎进到首页!<br>
<a href="/add">add</a><br><a href="/update">update</a>
</body>

添加页面

<body>
添加页面
</body>

修改页面

<body>
修改页面
</body>

跳转页面的​​controller​​方法

@RequestMapping("/")
public String shouye(){
return "index";
}
@RequestMapping("/add")
public String add(){
return "add";
}
@RequestMapping("/update")
public String update(){
return "update";
}

准备工作完成。

五,​​Shiro​​实现登录拦截

先创建一个登录页面和跳转该登录页面的​​controller​​方法。

登录页面

<body>
<form action="/login" method="post">
账号: <input type="text" ><br>
密码:<input type="password"><br>
<input type="submit" value="登录">
</form>
</body>

​controller​​方法

@RequestMapping("/login")
public String login(){
return "login";
}

在​​ShiroConfig​​​类的​​ShiroFilterFactoryBean​​​方法中写​​shiro​​的内置过滤器。

@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultSecurityManager defaultSecurityManager){
ShiroFilterFactoryBean factoryBean=new ShiroFilterFactoryBean();
//设置安全管理器
factoryBean.setSecurityManager(defaultSecurityManager);
//添加shiro的内置过滤器
/**
* anon:无需认证就可以访问
* authc:必须认证了才能访问
* user:必须拥有 记住我 功能才可以访问
* perms:拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
*/
Map<String,String> filterMap=new LinkedHashMap<>();
//设置必须认证才能访问
filterMap.put("/add","authc");
filterMap.put("/update","authc");
// filterMap.put("/update/*","authc");/*是通配符
factoryBean.setFilterChainDefinitionMap(filterMap);
//设置登录请求(必须认证才能访问,会自动跳转到下面的登录路径)
factoryBean.setLoginUrl("/login");
return factoryBean;
}

实现的效果如下图

进入首页

Shiro安全框架_数据库_07

点击​​add​​​按钮和​​update​​按钮,会让你去登录页面。(认证才能访问)

Shiro安全框架_数据_08

六,​​Shiro​​实现用户认证

首先在​​UserRealm​​类的认证方法中写认证方法。

//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证---一执行登录方法就进到此认证方法中");
//用户名 密码 数据库中取
//这里做假数据
String username="root";
String password="123456";
UsernamePasswordToken userToken=(UsernamePasswordToken)token;
if(!userToken.getUsername().equals(username)){
return null; //抛出异常 UnknownAccountException
}
//密码认证 是shiro自动管理密码的
return new SimpleAuthenticationInfo(username,password,"UserRealm");
}

然后写一个登录方法

@RequestMapping("/denglu")
public String denglu(String username, String password, Model model){
//获取当前的用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token=new UsernamePasswordToken(username,password);
//执行登录方法,没有异常则OK
try {
subject.login(token);
return "/index";
}catch (UnknownAccountException e){
model.addAttribute("msg","用户名不存在");
return "/login";
}catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码错误");
return "/login";
}
}

登录表单

回顾一下登录页面

<body>
<span th:text="${msg}"></span>
<form action="/denglu" method="post">
账号: <input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>

实现的效果如下图

首先进到首页

Shiro安全框架_数据库_09

点击​​add​​​按钮或者​​update​​按钮,会让你去认证,跳转登录页面。

Shiro安全框架_数据库_10

输入一个错误的用户名

Shiro安全框架_Shiro_11

输入一个错误的密码

Shiro安全框架_数据_12

输入正确,跳转到首页,认证成功。

Shiro安全框架_用户名_13

七,​​Shiro​​​整合​​mybatis​

​Shiro​​​整合​​mybatis​​​的过程就不讲了,重点是从数据库中的​​user​​账号密码表查询数据,而不是假数据。

依赖注入是在​​UserRealm​​的认证方法中,参考下面代码。

//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证---一执行登录方法就进到此认证方法中");
//用户名 密码 数据库中取
//这里做假数据
/**
* 当连接真实的数据库时,依赖注入service层以后,去数据库拿用户名对应的账号和密码那一条数据,参数是userToken.getUsername();
* 比如
* @Autowired
* Service service;
*
*
* User user=service.selectUserByUsername(userToken.getUsername());
* if(user==null){ //没有这个人
* return null; UnknownAccountException
* }
*
* //密码加密 Md5 Md5盐值加密
*
* return new SimpleAuthenticationInfo(user.getAccount(),user.getPwd(),"UserRealm");
*/
String username="root";
String password="123456";
UsernamePasswordToken userToken=(UsernamePasswordToken)token;
if(!userToken.getUsername().equals(username)){
return null; //抛出异常 UnknownAccountException
}
//密码认证 是shiro自动管理密码的
return new SimpleAuthenticationInfo(username,password,"UserRealm");
}

八,​​Shiro​​请求授权实现

首先在​​ShiroConfig​​​类的​​getShiroFilterFactoryBean​​方法中设置权限。

@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultSecurityManager defaultSecurityManager){
ShiroFilterFactoryBean factoryBean=new ShiroFilterFactoryBean();
//设置安全管理器
factoryBean.setSecurityManager(defaultSecurityManager);
//添加shiro的内置过滤器
/**
* anon:无需认证就可以访问
* authc:必须认证了才能访问
* user:必须拥有 记住我 功能才可以访问
* perms:拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
*/
Map<String,String> filterMap=new LinkedHashMap<>();
//设置必须认证才能访问
filterMap.put("/add","authc");
filterMap.put("/update","authc");
// filterMap.put("/update/*","authc");/*是通配符
//#######################################设置访问权限开始####################################
//授权 拥有对某个资源的权限才能访问
//正常情况下,没有授权会跳转到未授权页面
filterMap.put("/add","perms[user:add]");
//#######################################设置访问权限结束####################################
factoryBean.setFilterChainDefinitionMap(filterMap);
//设置登录请求(必须认证才能访问,会自动跳转到下面的登录路径)
factoryBean.setLoginUrl("/login");
//#######跳转未授权页面########
//未授权页面
factoryBean.setUnauthorizedUrl("/noauth");
//#######跳转未授权页面########
return factoryBean;
}

添加一个未授权页面的模拟方法

@RequestMapping("/noauth")
@ResponseBody
public String noauth(){
return "未经授权无法访问该页面";
}

访问权限已经设置好了,那么在哪里给用户进行授权呢?

说白了,在自定义​​Realm​​中的授权方法中进行授权。

这里提及一点,很重要!在​​UserRealm​​​的认证方法最后返回一个​​SimpleAuthenticationInfo​​​对象(下面)。第二个参数是用户的密码,第三个是自定义​​Realm​​的名字,重点是第一个参数。


return new SimpleAuthenticationInfo(user,password,"UserRealm");


这个参数在认证方法中返回,然后会传到当前类​​Realm​​的授权方法中,接收该参数的方法


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


开始授权

前提

在自定义的​​UserRealm​​类的认证方法中查询到当前用户的信息之后要把这个用户信息对象放到第一个参数里传到授权方法中并接收。

Shiro安全框架_数据库_14

然后再授权方法中拿到传过来的​​User​​对象。

Shiro安全框架_数据库_15

在自定义的​​UserRealm​​类的授权方法中进行授权。


不要迷! currentUser.getPerms()只是实体类的属性,即拿到数据库保存权限的字段的值,并不是shiro的方法。


//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行了授权---授权操作会首先进到此授权方法");
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//授权
//这里每进来申请权限的用户都会添加这个user:add操作权限,实际开发中要判断用户身份添加不同的操作权限
//这是写死的添加权限,连接数据库后,从数据库查询用户对应的权限,就是下面注释的内容,实现了动态授权
info.addStringPermission("user:add");
/**
* //拿到当前登录的对象
* Subject subject = SecurityUtils.getSubject();
* //拿到User对象 subject.getPrincipal()得到上面的第一个参数
* User currentUser =(User) subject.getPrincipal();
* //设置当前用户的权限
* info.addStringPermission(currentUser.getPerms());
*/
return info;
}

然后授权就完成了!

效果展示(在授权方法中没有执行授权时)

127.0.0.1:8088/index进入首页

Shiro安全框架_Shiro_16

点击按钮,转去认证(也就是登录)

Shiro安全框架_用户名_17

登录成功,返回首页

Shiro安全框架_数据_18

再点击​​update​​按钮,进入修改页面

Shiro安全框架_Shiro_19

点击​​add​​按钮,跳转未授权页面。

Shiro安全框架_数据_20

在​​UserRealm​​的授权方法中进行动态授权后(数据库查询用户对应的权限),就可以访问该页面了。

九,​​Shiro​​​整合​​thymeleaf​

这个业务场景是由于一些人没有一些权限,那么没有权限的操作列表就没有必要在页面上进行显示了,只有对应权限的用户才能看到该操作列表。

这里提一下​​shiro​​​如何记录​​session​​。

如果登录成功,记入session(此session是shiro环境的session,不是HttpSession)。在​​Realm​​类的认证方法中验证,如果用户密码正确,记入session。

Shiro安全框架_数据库_21

添加​​shiro​​​整合​​thymeleaf​​的依赖包

<!--shiro和thymeleaf整合-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>

然后在​​html​​​标签添加,和​​thymeleaf​​的格式一样。


xmlns:shiro="​​http://www.pollix.at/thymeleaf/shiro​


在ShiroConfig的配置类中加入Bean

@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}

首页的​​html​​代码

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
欢迎进到首页!<br>
<a shiro:hasPermission="add" href="/shiro/add">add</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a shiro:hasPermission="update" href="/shiro/edit">edit</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a style="float: right;margin-right: 30%;" href="/shiro/logout">退出</a>
</body>
</html>

说明:​​shiro:hasPermission="user:add"​​​是当用户有​​user:add​​权限时才展示在页面,否则不展示。

到此结束!之后在项目中碰到问题会接着在这里补充!



作者:樊同学