springboot+shiro+vue实现权限管理
- 一 功能简介:
-
- 1 介绍:
- 二 开始接入:
- 1 数据库设计
-
- 2 导入依赖
- 3 ShiroConfiguration : 设置拦截
- 4 MyShiroRealm: 用户权限认证
- 5 LoginService: 用户登录/退出
- 6 权限使用
- 7 权限permission编码
一 功能简介:
1 介绍:
Apache Shiro是Java的一个安全框架。对比另一个安全框架Spring Sercurity,它更简单和灵活。 Shiro可以帮助我们完成:认证、授权、加密、会话管理、Web集成、缓存等 2 shiro架构: 对于一个好的框架,从外部看来应该具有非常简单易于使用的API,且API契约明确;从内部来看的话,其应该有一个可扩展的架构,即非常容易插入用户自定义实现,因为任何框架都不能满足所有需求。
二 开始接入:
1 数据库设计
manager:role 关系 n:n
role:permission 关系 n:n
2 导入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
3 ShiroConfiguration : 设置拦截
private String APP = ""; //项目前缀 - 过滤拦截的时候使用
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
* 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
* 为了可以使用@RequiresPermissions标签
* 必须放在最前面
*
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
//加入注解的使用,不加入这个注解不生效
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
//将自己的验证方式加入容器
@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<String, String> map = new HashMap<String, String>();
/**
* 基于DefaultFilter
anon:所有url都都可以匿名访问;
authc: 需要认证才能进行访问;
user:配置记住我或认证通过可以访问;
*/
//登出
map.put(APP+ "/logout", "logout");
//对所有用户认证
//不拦截
map.put(APP+ "/index.html", "anon");
map.put(APP+ "/v1/login/**", "anon");
//拦截
map.put(APP+ "/page/intercept/index.html", "authc");
map.put(APP+ "/page/intercept/**", "authc");
//权限放行
map.put(APP+ "/page/intercept/dog3.jpg", "perms[permission:operlog_read]");
map.put(APP+ "/swagger-ui.html", "perms[permission:permission_write]");
//登录
shiroFilterFactoryBean.setLoginUrl(APP+ "/page/login.html"); //拦截后跳转到此页面
//首页,登录之后跳转
shiroFilterFactoryBean.setSuccessUrl(APP+ "/page/intercept/index.html"); // 登陆成功后跳转的页面
//错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl(APP+ "/page/error.html"); //找不到的页面指定跳转
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
}
4 MyShiroRealm: 用户权限认证
/**
* Created by zhiji on 2018/10/25.
* 进行权限信息处理
*/
public class MyShiroRealm extends AuthorizingRealm {
// 用于用户查询
@Autowired
private ManagerService managerService;
/**
* 1 用户访问URL的时候,查询用户权限,并绑定session
*
* @param authenticationToken
* @return 用户信息
* @throws AuthenticationException 捕获异常,用户是否登陆成功
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
long start = System.currentTimeMillis();
//加这一步的目的是在Post请求的时候会先进认证,然后在到请求
if (authenticationToken.getPrincipal() == null) {
return null;
}
//获取用户信息
String account = authenticationToken.getPrincipal().toString();
ShiroManagerGetOutDTO shiroManagerGetInDTO = managerService.selectByAccount(account);
if (shiroManagerGetInDTO == null) {
//这里返回后会报出对应异常
return null;
} else {
//这里验证authenticationToken和simpleAuthenticationInfo的信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(account, shiroManagerGetInDTO.getPassword().toString(), getName());
long end = System.currentTimeMillis();
System.out.println(MyShiroRealm.class + " doGetAuthenticationInfo 登录认证 ---------- 总耗时 " + (end - start) + "ms");
return simpleAuthenticationInfo;
}
}
/**
* 2 用户登陆的时候,数据库查询用户信息
* 增加角色
*
* @param principalCollection 传递过来的用户信息
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
long start = System.currentTimeMillis();
//1 获取登录用户名
String account = (String) principalCollection.getPrimaryPrincipal();
//2 查询用户名称
ShiroManagerGetOutDTO manager = managerService.selectByAccount(account);
if (StringUtils.isEmpty(manager))
return null;
//3 添加角色和权限
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
ShiroRoleGetOutDTO role = manager.getShiroRoleGetInDTO();
if (StringUtils.isEmpty(role))
return null;
authorizationInfo.addRole(role.getName()); // 给用户添加角色
List<ShiroPermissionGetOutDTO> permissions = role.getShiroPermissionGetInDTOList();
if (StringUtils.isEmpty(permissions) || permissions.size() <= 0)
return null;
for (ShiroPermissionGetOutDTO permission : permissions) {
authorizationInfo.addStringPermission(permission.getPermission()); // 添加用户的权限
}
long end = System.currentTimeMillis();
System.out.println(MyShiroRealm.class + " doGetAuthorizationInfo ---------- 总耗时 " + (end - start) + "ms");
return authorizationInfo;
}
/**
* 清除所有用户授权信息缓存.
*/
public void clearCachedAuthorizationInfo(String principal) {
SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
clearCachedAuthorizationInfo(principals);
}
/**
* @return void 返回类型
* @Title: clearAuthz
* @Description: TODO 清楚缓存的授权信息
*/
public void clearAuthz() {
this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
}
}
5 LoginService: 用户登录/退出
public void login(ShiroLoginInDTO manager) {
ShiroLoginInOutDTO shiroLoginInOutInDTO = new ShiroLoginInOutDTO();
//添加用户认证信息
Subject subject = SecurityUtils.getSubject();
String account = manager.getAccount();
String password = AppMD5Util.MD5Repeated20(manager.getPassword()); //密码统一使用MD5加密
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(manager.getAccount(), password);
subject.login(usernamePasswordToken);
subject.getSession().setTimeout(properties.getPermissionSessionTimeout()); //用户有效登录时长: 1 hours
}
public void logout() {
Subject subject = SecurityUtils.getSubject();
subject.logout(); //用户退出
subject.getSession().setTimeout(0);
}
6 权限使用
在controller层加入相应的标签即可
@RequiresPermissions(value = {“permission:app_read”, “permission:app_write”},logical = Logical.OR )
@RequiresRoles(“admin”)
7 权限permission编码
0:无
1:只读 read
2:读写 write
模块码 名称 编码 编码字符串
10 模块一
100 无权限
101 permission:permission_read
102 permission:permission_write
11 模块二
110 无权限
111 permission:upload_read
112 permission:upload_write
12 模块三
120 无权限
121 permission:publicity_read
122 permission:publicity_write
代码待完善,持续更新。。。
1 将用户token存储到redis中、可以多节点部署
2 自定义token生成规则
3 shiro全局异常捕获