Springboot 整合Shiro 轻量级权限框架,从数据库设计开始带你快速上手shiro

port: 8077

spring:

datasource:

druid:

username: root

password: root

url: jdbc:mysql://localhost:3306/my_system?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull

initialSize: 5

minIdle: 5

maxActive: 20

maxWait: 60000

timeBetweenEvictionRunsMillis: 60000

minEvictableIdleTimeMillis: 300000

validationQuery: SELECT 1 FROM DUAL

testWhileIdle: true

testOnBorrow: false

testOnReturn: false

poolPreparedStatements: true

maxPoolPreparedStatementPerConnectionSize: 20

useGlobalDataSourceStat: true

connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

mybatis:

config-location: classpath:mybatis/mybatis-config.xml

mapper-locations: classpath:mybatis/mapper/*.xml

接下来是对照数据库表结构,创建三个pojo:

SysUser.java

import lombok.Data;

/**

  • @Author : JCccc

  • @CreateTime : 2020/4/24

  • @Description :

**/

@Data

public class SysUser {

private Integer userId;

private String userName;

private String password;

private String userRemarks;

}

SysRole.class

import lombok.Data;

/**

  • @Author : JCccc

  • @CreateTime : 2020/4/24

  • @Description :

**/

@Data

public class SysRole {

private String roleId;

private String roleName;

private String roleRemarks;

}

SysPermissions.java

import lombok.Data;

/**

  • @Author : JCccc

  • @CreateTime : 2020/4/24

  • @Description :

**/

@Data

public class SysPermissions {

private Integer perId;

private String permissionsName;

private String perRemarks;

}

实体类创建完毕,先不用管那些操作表的增删改查。

核心使用环节, 创建ShiroConfig.java :

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.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import java.util.HashMap;

import java.util.Map;

/**

  • @Author : JCccc

  • @CreateTime : 2020/4/24

  • @Description :

**/

@Configuration

public class ShiroConfig {

@Bean

@ConditionalOnMissingBean

public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {

DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();

defaultAAP.setProxyTargetClass(true);

return defaultAAP;

}

//将自己的验证方式加入容器

@Bean

public UserRealm myShiroRealm() {

UserRealm userRealm = new UserRealm();

return userRealm;

}

//权限管理,配置主要是Realm的管理认证

@Bean

public DefaultWebSecurityManager securityManager() {

DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

securityManager.setRealm(myShiroRealm());

return securityManager;

}

//对url的过滤筛选

@Bean

public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {

ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

shiroFilterFactoryBean.setSecurityManager(securityManager);

Map<String, String> map = new HashMap<>();

//登出

map.put(“/logout”, “logout”);

//对所有用户认证

map.put(“/**”, “authc”);

//登录

shiroFilterFactoryBean.setLoginUrl(“/login”);

//成功登录后跳转的url

//shiroFilterFactoryBean.setSuccessUrl(“/xxxx”);

//错误页面,认证不通过跳转

// shiroFilterFactoryBean.setUnauthorizedUrl(“/error”);

shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

return shiroFilterFactoryBean;

}

@Bean

public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {

AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();

authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);

return authorizationAttributeSourceAdvisor;

}

}

创建自定义的权限审核处理类,UserRealm.java(涉及到数据库的查询文章后面有写):

import com.jc.shiro.pojo.SysUser;

import com.jc.shiro.service.LoginService;

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 java.util.List;

import java.util.Map;

/**

  • @Author : JCccc

  • @CreateTime : 2020/4/24

  • @Description :

**/

public class UserRealm extends AuthorizingRealm {

@Autowired

private LoginService loginService;

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

//获取登录用户名

String userName = (String) principalCollection.getPrimaryPrincipal();

//添加角色和权限

SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

List<Map<String, Object>> powerList = loginService.getUserPower(userName);

System.out.println(powerList.toString());

for (Map<String, Object> powerMap : powerList) {

//添加角色

simpleAuthorizationInfo.addRole(String.valueOf(powerMap.get(“roleName”)));

//添加权限

simpleAuthorizationInfo.addStringPermission(String.valueOf(powerMap.get(“permissionsName”)));

}

return simpleAuthorizationInfo;

}

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

//加这一步的目的是在Post请求的时候会先进认证,然后在到请求

if (authenticationToken.getPrincipal() == null) {

return null;

}

//获取用户信息

String userName = authenticationToken.getPrincipal().toString();

//根据用户名去数据库查询用户信息

SysUser sysUser = loginService.queryUser(userName);

if (sysUser == null) {

//这里返回后会报出对应异常

return null;

} else {

//这里验证authenticationToken和simpleAuthenticationInfo的信息

SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, sysUser.getPassword().toString(), getName());

return simpleAuthenticationInfo;

}

}

}

然后是我们的登录接口,这里融合自己项目的帐号加密方法,LoginController.java:

import com.jc.shiro.service.LoginService;

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.AuthenticationException;

import org.apache.shiro.authc.UsernamePasswordToken;

import org.apache.shiro.authz.AuthorizationException;

import org.apache.shiro.subject.Subject;

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

import org.springframework.stereotype.Controller;

import org.springframework.util.DigestUtils;

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

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

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

/**

  • @Author : JCccc

  • @CreateTime : 2020/4/24

  • @Description :

**/

@Controller

public class LoginController {

@Autowired

LoginService loginService;

@ResponseBody

@GetMapping(“/login”)

public String login(@RequestParam(“userName”) String userName, @RequestParam(“password”) String password) {

//添加用户认证信息

Subject subject = SecurityUtils.getSubject();

//自己系统的密码加密方式 ,这里简单示例一下MD5

String md5Password = DigestUtils.md5DigestAsHex(password.getBytes());

UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userName, md5Password);

try {

//进行验证,AuthenticationException可以catch到,但是AuthorizationException因为我们使用注解方式,是catch不到的,所以后面使用全局异常捕抓去获取

subject.login(usernamePasswordToken);

} catch (AuthenticationException e) {

e.printStackTrace();

return “账号或密码错误!”;

} catch (AuthorizationException e) {

e.printStackTrace();

return “没有权限”;

}

return “login success”;

}

}

其中用到了一个根据用户登录帐号去查询对应的角色权限信息,也就是文章开头我们设计的查询。

mapper层:

LoginMapper.java

import com.jc.shiro.pojo.SysUser;

import org.apache.ibatis.annotations.Mapper;

import java.util.List;

import java.util.Map;

/**

  • @Author : JCccc

  • @CreateTime : 2020/4/24

  • @Description :

**/

@Mapper

public interface LoginMapper {

SysUser queryUser(String userName );

List<Map<String,Object>> getUserPower(String userName);

}

loginMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>

SELECT *

FROM sys_user

WHERE userName=#{userName}

SELECT user.userId ,user.userName,role.roleName,role.roleId,per.permissionsName ,per.perId,per.perRemarks

FROM sys_user AS user,

sys_role AS role,

sys_permissions AS per,

role_per,

user_role

WHERE user.userName=#{userName}

AND user.userId=user_role.userId

AND user_role.roleId=role.roleId

AND role_per.roleId=role.roleId

AND role_per.perId=per.perId

然后是service层:

LoginService.java

import com.jc.shiro.pojo.SysUser;

import java.util.List;

import java.util.Map;

/**

  • @Author : JCccc

  • @CreateTime : 2020/4/24

  • @Description :

**/

public interface LoginService {

SysUser queryUser(String userName );

List<Map<String,Object>> getUserPower(String userName );

}

LoginServiceImpl.java

import com.jc.shiro.mapper.LoginMapper;

import com.jc.shiro.pojo.SysUser;

import com.jc.shiro.service.LoginService;

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

import org.springframework.stereotype.Service;

import java.util.List;

import java.util.Map;

/**

  • @Author : JCccc

  • @CreateTime : 2020/4/24

  • @Description :

**/

@Service

public class LoginServiceImpl implements LoginService {

@Autowired

LoginMapper loginMapper;

@Override

public SysUser queryUser(String userName) {

return loginMapper.queryUser(userName);

}

@Override

public List<Map<String, Object>> getUserPower(String userName) {

return loginMapper.getUserPower(userName);

}

}

最后再补上一个异常全局控制器,MyExceptionHandler.java:

import lombok.extern.slf4j.Slf4j;

import org.apache.shiro.authz.AuthorizationException;

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

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

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

/**

  • @Author : JCccc

  • @CreateTime : 2020/4/24

  • @Description :

**/

@ControllerAdvice

@Slf4j

public class MyExceptionHandler {

@ExceptionHandler

@ResponseBody

public String ErrorHandler(AuthorizationException e) {

log.error(“权限校验失败!”, e);

return “您暂时没有权限,请联系管理员!”;

}

}

到这里,已经整合完毕,接下来是我们的使用,后端的校验我们采取shiro提供的注解的方式去使用:

@RequiresRoles(“xxx”)

@RequiresPermissions(“xxx”)

我们简单模拟导出数据功能模块,ExportController.java:

暂时只提供了一个导出接口,而这个导出接口需要用户拥有 admin 角色 以及 exportUserInfo 权限,也就是代码里注解的参数

import org.apache.shiro.authz.annotation.RequiresPermissions;

import org.apache.shiro.authz.annotation.RequiresRoles;

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

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

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

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

块,ExportController.java:

暂时只提供了一个导出接口,而这个导出接口需要用户拥有 admin 角色 以及 exportUserInfo 权限,也就是代码里注解的参数

import org.apache.shiro.authz.annotation.RequiresPermissions;

import org.apache.shiro.authz.annotation.RequiresRoles;

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

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

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

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-XnvVMDXV-1710891639651)]
[外链图片转存中…(img-WBgywD8c-1710891639652)]
[外链图片转存中…(img-POAyMuS0-1710891639653)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-bPjAEgyY-1710891639653)]

  • 15
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值