Spring Boot:整合Shiro权限框架
2020-12-13 05:36
标签:lease autowired 访问权限 xsd cep tran att ade 加密算 Shiro是Apache旗下的一个开源项目,它是一个非常易用的安全框架,提供了包括认证、授权、加密、会话管理等功能,与Spring Security一样属基于权限的安全框架,但是与Spring Security 相比,Shiro使用了比较简单易懂易于使用的授权方式。Shiro属于轻量级框架,相对于Spring Security简单很多,并没有security那么复杂。 它是一个功能强大、灵活的、优秀的、开源的安全框架。 它可以胜任身份验证、授权、企业会话管理和加密等工作。 它易于使用和理解,与Spring Security相比,入门门槛低。 Shiro 的整体框架大致如下图所示(图片来自互联网): Authentication(认证), Authorization(授权), Session Management(会话管理), Cryptography(加密)代表Shiro应用安全的四大基石。 它们分别是: 除此之外,还有其他的功能来支持和加强这些不同应用环境下安全领域的关注点。 特别是对以下的功能支持: 在概念层,Shiro 架构包含三个主要的理念:Subject, SecurityManager 和 Realm。下面的图展示了这些组件如何相互作用,我们将在下面依次对其进行描述。 Shiro执行流程图(图片来自互联网) 三个主要理念: 我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。 接下来,我们就通过一个具体的案例,来讲解如何进行Shiro的整合,然后借助Shiro实现登录认证和访问控制。 为方便我们初始化项目,Spring Boot给我们提供一个项目模板生成网站。 1. 打开浏览器,访问:https://start.spring.io/ 2. 根据页面提示,选择构建工具,开发语言,项目信息等。 3. 点击 Generate the project,生成项目模板,生成之后会将压缩包下载到本地。 4. 使用IDE导入项目,我这里使用Eclipse,通过导入Maven项目的方式导入。 清理掉不需要的测试类及测试依赖,添加 Maven 相关依赖,这里需要添加上WEB、Swagger、JPA和Shiro的依赖,Swagger的添加是为了方便接口测试。 pom.xml 1.添加数据源和jpa相关配置 将application.properties文件改名为application.yml ,并在其中添加MySQL数据源连接信息。 注意: 这里需要首先创建一个MySQL数据库,并输入自己的用户名和密码。这里的数据库是springboot。 另外,如果你使用的是MySQL 5.x及以前版本,驱动配置driverClassName是com.mysql.jdbc.Driver。 application.yml 2. 添加swagger 配置 添加一个swagger 配置类,在工程下新建 config 包并添加一个 SwaggerConfig 配置类。 SwaggerConfig.java 添加一个用户类User,包含用户名和密码,用来进行登录认证,另外用户可以拥有角色。 User.java 添加一个角色类Role,表示用户角色,角色拥有可操作的权限集合。 Role.java 添加一个权限类Permission,表示资源访问权限。 Permission.java 添加一个DAO基础接口,用来被其他DAO继承。 BaseDao.java 添加一个UserDao,用来操作用户信息。 UserDao.java 添加一个RoleDao,用来操作角色信息。 RoleDao.java 添加一个LoginService服务接口。 LoginService.java 添加一个LoginServiceImpl,实现服务功能,这里为了方便,在插入角色的时候会默认设置其权限。 LoginServiceImpl.java 添加一个登录控制器,编写相关的接口。create接口添加了@RequiresPermissions("create"),用于进行权限注解测试。 LoginController.java 添加一个MyShiroRealm并继承AuthorizingRealm,实现其中的两个方法。 doGetAuthenticationInfo:实现用户认证,通过服务加载用户信息并构造认证对象返回。 doGetAuthorizationInfo:实现权限认证,通过服务加载用户角色和权限信息设置进去。 MyShiroRealm.java 添加一个Shiro配置类,主要配置路由的访问控制,以及注入自定义的认证器MyShiroRealm。 ShiroConfig.java 1. 右键项目 -> Run as -> Maven install,开始执行Maven构建,第一次会下载Maven依赖,可能需要点时间,如果出现如下信息,就说明项目编译打包成功了。 2. 右键文件 DemoApplication.java -> Run as -> Java Application,开始启动应用,如果一开始数据库没有对应的表,在应用启动时会创建,我们可以通过控制台查看到对应的SQL语句。 3. 打开浏览器,访问:http://localhost:8080/swagger-ui.html,进入swagger接口文档界面。 4. 首先用MySQL客户端,打开数据库,往用户表里面插入一条记录,{id=1, name="admin", password="123"}。 然后试着用Swagger调用addUser往用户表插入一条记录。 结果返回"error page!",这是因为我们没有登录,还没有操作权限。 接着调用POST的login接口,输入以下用户信息进行登录。 登录成功之后,返回“login ok!”信息。 再次调用addUser往用户表插入记录,发现记录已经可以成功插入了。 通过客户端工具我们也可以查看到记录已经插入进来了。 通过上面的测试,我们已经成功的验证了,受保护的接口需要在登录之后才允许访问。接下来我们来测试一下权限注解的效果,我们在create方法上加上了权限注解@RequiresPermissions("create"),表示用户需要拥有"create"的权限才能访问。 先尝试调用以下create接口,发现尽管我们已经登录了,依然因为没有权限返回了“error page!”。 然后我们调用addRole插入以下角色记录,这个角色关联了我们当前登录admin用户,且角色在创建时我们代码默认设置拥有了“create”权限。 如果执行正确的话,会返回如下信息,说明角色已经成功插入了。 然后我们再一次调用create接口,因为此刻admin用户拥有admin角色,而admin角色拥有“create”权限,所以已经具有接口访问权限了。 项目主页:http://shiro.apache.org/ W3C资料:https://www.w3cschool.cn/shiro/ 百度百科:https://baike.baidu.com/item/shiro/17753571?fr=aladdin Spring Boot:整合Shiro权限框架 标签:lease autowired 访问权限 xsd cep tran att ade 加密算 原文地址:https://www.cnblogs.com/7788IT/p/11143519.html综合概述
优势特点
主要功能
框架体系
主要流程
实现案例
生成项目模板
添加相关依赖
添加相关配置
server:
port: 8080
spring:
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8
username: root
password: 123456
jpa:
show-sql: true # 默认false,在日志里显示执行的sql语句
database: mysql
hibernate.ddl-auto: update #指定为update,每次启动项目检测表结构有变化的时候会新增字段,表不存在时会新建,如果指定create,则每次启动项目都会清空数据并删除表,再新建
properties.hibernate.dialect: org.hibernate.dialect.MySQL5Dialect
database-platform: org.hibernate.dialect.MySQL5Dialect
hibernate:
naming:
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl #指定jpa的自动表生成策略,驼峰自动映射为下划线格式
#physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
package com.louis.springboot.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any()).build();
}
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("SpringBoot API Doc")
.description("This is a restful api document of Spring Boot.")
.version("1.0")
.build();
}
}
编写业务代码
package com.louis.springboot.demo.model;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(unique = true)
private String name;
private String password;
@OneToMany(cascade = CascadeType.ALL,mappedBy = "user")
private List
package com.louis.springboot.demo.model;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
@Entity
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String roleName;
@ManyToOne(fetch = FetchType.EAGER)
private User user;
@OneToMany(cascade = CascadeType.ALL,mappedBy = "role")
private List
package com.louis.springboot.demo.model;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class Permission {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String permission;
@ManyToOne(fetch = FetchType.EAGER)
private Role role;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
package com.louis.springboot.demo.dao;
import java.io.Serializable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
@NoRepositoryBean
public interface BaseDao
package com.louis.springboot.demo.dao;
import com.louis.springboot.demo.model.User;
public interface UserDao extends BaseDao
package com.louis.springboot.demo.dao;
import com.louis.springboot.demo.model.Role;
public interface RoleDao extends BaseDao
package com.louis.springboot.demo.service;
import com.louis.springboot.demo.model.Role;
import com.louis.springboot.demo.model.User;
public interface LoginService {
User addUser(User user);
Role addRole(Role role);
User findByName(String name);
}
package com.louis.springboot.demo.service.impl;
import java.util.ArrayList;
import java.util.List;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.louis.springboot.demo.dao.RoleDao;
import com.louis.springboot.demo.dao.UserDao;
import com.louis.springboot.demo.model.Permission;
import com.louis.springboot.demo.model.Role;
import com.louis.springboot.demo.model.User;
import com.louis.springboot.demo.service.LoginService;
@Service
@Transactional
public class LoginServiceImpl implements LoginService {
@Autowired
private UserDao userDao;
@Autowired
private RoleDao roleDao;
//添加用户
@Override
public User addUser(User user) {
userDao.save(user);
return user;
}
//添加角色
@Override
public Role addRole(Role role) {
User user = userDao.findByName(role.getUser().getName());
role.setUser(user);
Permission permission1 = new Permission();
permission1.setPermission("create");
permission1.setRole(role);
Permission permission2 = new Permission();
permission2.setPermission("update");
permission2.setRole(role);
List
package com.louis.springboot.demo.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.louis.springboot.demo.model.Role;
import com.louis.springboot.demo.model.User;
import com.louis.springboot.demo.service.LoginService;
@RestController
public class LoginController {
@Autowired
private LoginService loginService;
/**
* POST登录
* @param map
* @return
*/
@PostMapping(value = "/login")
public String login(@RequestBody User user) {
// 添加用户认证信息
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getName(), user.getPassword());
// 进行验证,这里可以捕获异常,然后返回对应信息
SecurityUtils.getSubject().login(usernamePasswordToken);
return "login ok!";
}
/**
* 添加用户
* @param user
* @return
*/
@PostMapping(value = "/addUser")
public String addUser(@RequestBody User user) {
user = loginService.addUser(user);
return "addUser is ok! \n" + user;
}
/**
* 添加角色
* @param role
* @return
*/
@PostMapping(value = "/addRole")
public String addRole(@RequestBody Role role) {
role = loginService.addRole(role);
return "addRole is ok! \n" + role;
}
/**
* 注解的使用
* @return
*/
@RequiresRoles("admin")
@RequiresPermissions("create")
@GetMapping(value = "/create")
public String create() {
return "Create success!";
}
@GetMapping(value = "/index")
public String index() {
return "index page!";
}
@GetMapping(value = "/error")
public String error() {
return "error page!";
}
/**
* 退出的时候是get请求,主要是用于退出
* @return
*/
@GetMapping(value = "/login")
public String login() {
return "login";
}
@GetMapping(value = "/logout")
public String logout() {
return "logout";
}
}
package com.louis.springboot.demo.config;
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.springframework.beans.factory.annotation.Autowired;
import com.louis.springboot.demo.model.Permission;
import com.louis.springboot.demo.model.Role;
import com.louis.springboot.demo.model.User;
import com.louis.springboot.demo.service.LoginService;
/**
* 实现AuthorizingRealm接口用户用户认证
* @author Louis
* @date Jun 20, 2019
*/
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
private LoginService loginService;
/**
* 用户认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
// 加这一步的目的是在Post请求的时候会先进认证,然后在到请求
if (authenticationToken.getPrincipal() == null) {
return null;
}
// 获取用户信息
String name = authenticationToken.getPrincipal().toString();
User user = loginService.findByName(name);
if (user == null) {
// 这里返回后会报出对应异常
return null;
} else {
// 这里验证authenticationToken和simpleAuthenticationInfo的信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name,
user.getPassword().toString(), getName());
return simpleAuthenticationInfo;
}
}
/**
* 角色权限和对应权限添加
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获取登录用户名
String name = (String) principalCollection.getPrimaryPrincipal();
// 查询用户名称
User user = loginService.findByName(name);
// 添加角色和权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (Role role : user.getRoles()) {
// 添加角色
simpleAuthorizationInfo.addRole(role.getRoleName());
for (Permission permission : role.getPermissions()) {
// 添加权限
simpleAuthorizationInfo.addStringPermission(permission.getPermission());
}
}
return simpleAuthorizationInfo;
}
}
package com.louis.springboot.demo.config;
import java.util.HashMap;
import java.util.Map;
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.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfig {
// 将自己的验证方式加入容器
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
return myShiroRealm;
}
// 权限管理,配置主要是Realm的管理认证
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
// Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map
编译测试运行
{
"id": 2,
"name": "xiaoming",
"password": "123"
}
{
"name": "admin",
"password": "123"
}
{
"id": 1,
"roleName": "admin",
"user": {
"name": "admin"
}
}
参考资料