前言
springboot的配置和ssm的配置有所区别,但是它们本质上又是一样的。shiro的整合也是如此,虽然配置方式不一样,一个人xml中一个在java代码中,但是他们的流程还是差不多的,都是先配置凭证器->自定义realm->安全管理器->过滤器链等。所以在这篇博客中,我将参考前两天写的ssm整合shiro来进行springboot整合shiro的配置。
springboot基本配置 ----- 整合mybatis
pom.xml如下
<?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.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.qiu</groupId>
<artifactId>shirobyboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shirobyboot</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.1</version>
</dependency>
<!-- druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!-- shiro-thymeleaf 让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>
<!-- 让java文件下的xml文件能够编译进target文件中,程序运行过程中能够扫描到这里-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
文件目录和ssm整合shiro篇一致,截图如下,如有疑惑的伙伴可以点进超链接,在那篇文章中我已经略做说明了。
文件目录截图如下
application.yml配置如下
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/shiro_ssm?useUnicode=true&characterEncoding=utf8&autoReconnect=true&serverTimezone=GMT%2B8&useSSL=false
username: root
password: root
max-active: 20
max-wait: 6000
initial-size: 1
#禁用thymeleaf的缓存
thymeleaf:
cache: false
#定义mapper.xml文件的路径
mybatis:
mapper-locations: classpath:com/qiu/shirobyboot/mapper/xml/*.xml
springboot不像ssm可以自动创建出mapper接口的代理对象供其他bean注入,在springboot中想要创建出mapper接口的代理对象需要扫描,只需要如下配置
package com.qiu.shirobyboot;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.qiu.shirobyboot.mapper")
public class ShirobybootApplication {
public static void main(String[] args) {
SpringApplication.run(ShirobybootApplication.class, args);
}
}
当然如果不想配置这个@MapperScan,可以在每一个mapper接口上添加一个@Mapper,但是明显很麻烦。
package com.qiu.shirobyboot.mapper;
import com.qiu.shirobyboot.pojo.Permission;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @Author VULCAN
* @create 2020/1/17 20:34
*/
@Mapper
public interface PerMapper {
List<Permission> selPerByRole(@Param("roleid")Integer roleid);
}
在自动类上加@MapperScan和在mapper接口上加@Mapper都是同样的效果,伙伴们可以选择一种方式去创建mapper接口的代理对象,当然两种方式并存也没什么关系。
需要一提的是,在service实现层注入mapper接口的时候,会出现爆红现象,纯属正常不影响运行。
以上springboot整合mybatis基本整合完毕,开始测试。
mapper.xml部分sql如下
<select id="selRole" resultType="com.qiu.shirobyboot.pojo.Role">
select * from role
</select>
controller层接口代码如下
@RequestMapping("/test2")
public List<Role> test2(){
return roleService.selRole();
}
运行结果如下
开始shiro的整合
自定义realm的创建
其实和ssm整合shiro的自定义realm一样(我直接拷贝过来的)代码如下
MyRealm.java
package com.qiu.shirobyboot.config;
import com.qiu.shirobyboot.pojo.Permission;
import com.qiu.shirobyboot.pojo.Role;
import com.qiu.shirobyboot.pojo.User;
import com.qiu.shirobyboot.service.PerService;
import com.qiu.shirobyboot.service.RoleService;
import com.qiu.shirobyboot.service.UserService;
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.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.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @Author VULCAN
* @create 2020/1/19 17:10
*/
//MyRealm在配置文件中已经注册成bean了,所以可以在它的类下使用@Autowired
public class MyRealm extends AuthorizingRealm {
@Autowired
UserService userService;
@Autowired
RoleService roleService;
@Autowired
PerService perService;
@Override
public String getName(){
return this.getClass().getSimpleName();
}
//角色权限添加
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user = (User)principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
List<Role> roles = roleService.selRoleByUser(user.getUserid());
Collection<String> roleNames=new ArrayList<String>();
Collection<String> perCodes=new ArrayList<String>();
if(roles!=null && roles.size()>0){
for (Role role:roles){
String rolename = role.getRolename();
int roleid = role.getRoleid();
roleNames.add(rolename);
//获得所有权限功能
List<Permission> permissions = perService.selPerByRole(roleid);
if(permissions!=null && permissions.size()>0){
for (Permission permission:permissions){
String percode = permission.getPercode();
perCodes.add(percode);
}
}
}
//增加角色
info.addRoles(roleNames);
//增加权限功能
info.addStringPermissions(perCodes);
}
return info;
}
//信息验证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken info) throws AuthenticationException {
User users = userService.selUser(info.getPrincipal().toString());
if(users!=null){
String password = users.getPassword();
String username = users.getUsername();
String solt = users.getSolt();
ByteSource soltToByte=ByteSource.Util.bytes(solt);
//如果与前台输入的接口不匹配,则密码错误,抛出密码不匹配异常
SimpleAuthenticationInfo logininfo=new SimpleAuthenticationInfo(users,password,soltToByte,this.getName());
return logininfo;
}else{
//返回空就表示用户名不存在
return null;
}
}
}
shiro安全管理器、过滤器链等配置
package com.qiu.shirobyboot.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @Author VULCAN
* @create 2020/1/23 17:46
*/
@Configuration
public class ShiroConfig {
//匿名登录————指不需要登录(身份验证)就可以访问的路径
//匿名登录路径的数组,为后续过滤器链所提供
private String[] anonArr={"/startlogin","/login"};
//配置凭证匹配器MD5
@Bean
HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher=new HashedCredentialsMatcher();
//设置加密算法为md5
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//加密迭代次数为一次
hashedCredentialsMatcher.setHashIterations(1);
return hashedCredentialsMatcher;
}
//配置自己的realm
@Bean
MyRealm myRealm(){
MyRealm realm=new MyRealm();
//给自定义realm注入凭证匹配器
realm.setCredentialsMatcher(hashedCredentialsMatcher());
return realm;
}
//配置SecurityManager
@Bean
DefaultWebSecurityManager defaultSecurityManager(){
DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
//将自己的realm注入到安全管理器中
defaultSecurityManager.setRealm(myRealm());
return defaultSecurityManager;
}
//配置过滤器链
@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean(){
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
//key是路径,value是访问模式,给过滤器注入用
Map<String,String> map=new HashMap<>();
for (String anno:anonArr) {
map.put(anno,"anon");
}
map.put("/logout","logout");
map.put("/**","authc");
//将安全管理器配置进过滤器中,让过滤器生效
shiroFilterFactoryBean.setSecurityManager(defaultSecurityManager());
shiroFilterFactoryBean.setLoginUrl("/startlogin");
//配置过滤器链------路径访问模式
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
//让thymeleaf整合shiro的标签生效,在ssm里面,因为shiro自己已经提供了标签库,所以不用多余的配置,只需要在指定页面引入一下标签库就可以生效
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
//让shiro注解生效↓
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor= new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(defaultSecurityManager());
return authorizationAttributeSourceAdvisor;
}
//让shiro注解生效↑
// 我没有扒过源码,所以不知道为什么springboot整合shiro的时候不需要配置shiro过滤器,但是ssm还是需要的
}
由上述配置得知,/startlogin为访问login页的页面接口,当系统发现某个用户没有验证信息(未登录)将会强制跳转到/startlogin接口,登录且将用户信息保存进session的接口是/login,登出是/logout,除以上这些路径以外,其余的路径都需要身份认证。
controller层
仍旧和ssm整合shiro里面的controller一样
package com.qiu.shirobyboot.controller;
import com.qiu.shirobyboot.pojo.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
/**
* @Author VULCAN
* @create 2020/1/19 21:01
*/
@Controller
public class PageControlle {
//登录页,没有得到授权的页面都会跳到这个页面
@RequestMapping("startlogin")
public String runStartlogin(){
return "login";
}
//内容页,不过因为配置了过滤器,只有登录成功后才能访问
@RequestMapping("toContent")
public String toContent(){
return "content";
}
@RequestMapping("login")
public String login(String username , String password ,boolean rememberme, HttpSession httpSession){
//不需要绑定线程
//获得主体对象
Subject subject = SecurityUtils.getSubject();
//得到token信息
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//记住我功能
token.setRememberMe(rememberme);
//开始登录,进入myrealm中,如果返回为null则代表账户不存在,会抛出异常。匹配不上,会抛出异常
try {
subject.login(token);
User userinfo = (User)subject.getPrincipal();
httpSession.setAttribute("userinfo",userinfo);
//登录正确跳转到toContent的路径映射
return "redirect:toContent";
} catch (AuthenticationException e) { //CredentialsException密码与账户不匹配 //AccountException账户错误(不存在)
System.out.println("用户名或密码有错误");
httpSession.setAttribute("error","登录错误");
//登录错误跳转到startlogin的路径映射
return "redirect:startlogin";
}
}
}
login.html
thymeleaf模板的后缀仍然是.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
我是登录页
<form action="/login" method="post">
<input name="username" type="text" placeholder="请输入用户名">
<input name="password" type="password" placeholder="请输入密码">
<input type="submit" value="登录">
</form>
</body>
</html>
content.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</title>
</head>
<body>
<!--thymeleaf的el表达式如下,如果是session,好像需要强调是哪个作用域-->
我是内容<span th:text="${session.userinfo.username}"></span>
<!--访问的用户有user:sel权限就展现出来,没有就不显示。-->
<shiro:hasPermission name="user:sel">
<a>用户查询</a>
</shiro:hasPermission>
<shiro:hasPermission name="user:add">
<a>用户添加</a>
</shiro:hasPermission>
<shiro:hasPermission name="user:up">
<a>用户修改</a>
</shiro:hasPermission>
<shiro:hasPermission name="user:del">
<a>用户删除</a>
</shiro:hasPermission>
<shiro:hasPermission name="user:export">
<a>导出用户</a>
</shiro:hasPermission>
</body>
</html>
要注意,想要让shiro标签生效,需要在ShiroConfig.java代码中添加如下代码
//让thymeleaf整合shiro的标签生效,在ssm里面,因为shiro自己已经提供了标签库,所以不用多余的配置,只需要在指定页面引入一下标签库就可以生效
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
运行服务后页面如下
正确输入密码后跳转到/toContent接口
结果如下,很明显,qiu这个用户只有用户查询、用户添加、用户修改这三个功能
shiro相关注解起效说明
因为这次springboot整合shiro的时候我直接把注解失效问题解决了,所以以下仅做说明
代码如下,只有user:export权限的用户才能访问这个接口
由上得知,qiu这个用户没有user:export这个权限(用户导出),按理说是不能访问的
@RequiresPermissions("user:export")
@RequestMapping("/test")
public String test(){
return "test";
}
但是不做任何配置的话,qiu这个用户照样能访问到/test这个接口
所以相关配置如下
在ShiroConfig.java文件中添加以下代码
//让shiro注解生效↓
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor= new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(defaultSecurityManager());
return authorizationAttributeSourceAdvisor;
}
//让shiro注解生效↑
配置上述代码后,当用户强行访问没有权限的接口时,浏览器会跳转到500错误界面,所以为了美观,我们需要全局报错监控,当用户访问没有权限的接口时,返回一个字符串"noPower"
代码如下
MyExceptionHandle.java
package com.qiu.shirobyboot.ExceptionHandle;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @Author VULCAN
* @create 2020/1/22 15:27
*/
//全局异常监控,出现的异常就跑到这里来,但是需要在springmvc.xml中配置扫描到该注解
@ControllerAdvice
public class MyExceptionHandle {
@ResponseBody
@ExceptionHandler(value={UnauthorizedException.class})
public String dealUnauthorized() {
return "noPower";
}
}
来源:CSDN
作者:我fuze很6
链接:https://blog.csdn.net/weixin_44363422/article/details/104080507