shiro
用来做登录认证和权限管理,跟拦截器类似,可以设置未登录状态去到某个页面,登录后去到某个页面,可以设置权限标识,有权限标识的才能使用某些功能。
使用spring整合shiro来显示不同用户看到不同的菜单,以及不同权限拥有不同的操作
- 添加shiro依赖
<!-- shiro spring. -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!-- shiro core -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
- 在resources文件夹下新建一个spring文件夹下新建一个shiro.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- ref对应我们写的realm myRealm -->
<property name="realm" ref="AuthRealm" />
<!-- 使用下面配置的缓存管理器 -->
<!-- <property name="cacheManager" ref="shiroEncacheManager" /> -->
</bean>
<!-- 安全认证过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 调用我们配置的权限管理器 -->
<property name="securityManager" ref="securityManager" />
<!-- 配置我们的登录请求地址 -->
<property name="loginUrl" value="/toLogin" />
<!-- 配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址 -->
<property name="successUrl" value="/toIndex" />
<!-- 如果您请求的资源不再您的权限范围,则跳转到错误请求地址 -->
<property name="unauthorizedUrl" value="/toError" />
<!--anon都可以访问-->
<!--authc授权了后才可以访问-->
<property name="filterChainDefinitions">
<value>
/statics/**=anon
/login=anon
/** = authc
</value>
</property>
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-- AOP式方法级权限检查 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
</beans>
- 新建一个AuthRealm类继承AuthorizingRealm并重写方法,并交给spring容器管理
package com.ossjk.config.shiro;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ossjk.core.constant.Constant;
import com.ossjk.hui.system.entity.Permission;
import com.ossjk.hui.system.entity.User;
import com.ossjk.hui.system.service.IPermissionService;
import com.ossjk.hui.system.service.IUserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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 org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Component("AuthRealm")
public class AuthRealm extends AuthorizingRealm {
@Autowired
private IUserService iUserService;
@Autowired
private IPermissionService iPermissionService;
/**
* 授权
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取授权信息
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
System.out.println("000000000000000000000000000000000000000000");
List<String> urls = (List<String>) SecurityUtils.getSubject().getSession().getAttribute(Constant.SESSION_URLS);
//授权权限
simpleAuthorizationInfo.addStringPermissions(urls);
System.out.println("=====================");
for (String url:urls) {
System.out.println(url);
}
return simpleAuthorizationInfo;
}
/**
* 验证登陆
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//验证登陆 UsernamePasswordToken token = new UsernamePasswordToken(name,pwd);拿到构建登录时传来的账号密码
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
String username = usernamePasswordToken.getUsername();
//拿到的是一个字符数组,可便于加密操作
char[] password = usernamePasswordToken.getPassword();
//这里就不进行加密了直接转为字符串
String pwd = new String(password);
//调用service层查询用户
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.eq("name", username);
User user = iUserService.getOne(userQueryWrapper);
//判断用户是否存在
if(ObjectUtil.isNull(user)){
//用户不存在,抛异常
throw new UnknownAccountException();
}
//判断密码是否正确
if(!ObjectUtil.equal(user.getPwd(),pwd)){
//密码不正确,抛异常
throw new IncorrectCredentialsException();
}
//验证成功
//shiro获取session并将用户放到session中
SecurityUtils.getSubject().getSession().setAttribute(Constant.SESSION_USER,user);
//调用service层,查询用户的所有权限
List<Permission> permissions = iPermissionService.listByUid(user.getId());
//从所有权限中过滤出用户所拥有的菜单权限
List<Permission> menus = permissions.stream().filter(t -> {
return (t.getLevel().intValue() == 0) || (t.getLevel().intValue() == 1);
}).collect(Collectors.toList());
//将菜单放到session
SecurityUtils.getSubject().getSession().setAttribute(Constant.SESSION_MENUS,menus);
//从权限信息中过滤出用户所拥有的操作权限
List<String> urls = permissions.stream().filter(t -> {
return (t.getLevel().intValue() == 1 || t.getLevel().intValue() == 2);
}).map(t -> {
return t.getUrl();
}).collect(Collectors.toList());
//将用户操作权限放到session
SecurityUtils.getSubject().getSession().setAttribute(Constant.SESSION_URLS,urls);
//构造认证信息
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPwd(),getName());
return info;
}
}
- 在登陆方法内调用shiro认证登陆
/**
* 登录
*
* @param name
* @param pwd
* @return
*/
@RequestMapping("/login")
public String login(String name, String pwd, ModelMap map, HttpSession session) {
//构造shiro需要的登陆参数
UsernamePasswordToken token = new UsernamePasswordToken(name,pwd);
try{
//交给shiro去验证登陆
SecurityUtils.getSubject().login(token);
}catch (UnknownAccountException uae){
map.put("msg","未知用户");
return "login";
}catch (IncorrectCredentialsException ice){
map.put("msg","密码错误");
return "login";
}catch (AuthenticationException ae){
map.put("msg","服务器忙");
return "login";
}
//验证成功去到首页
return "redirect:/toIndex";
}
- 在Controller上添加权限判断的注解
/**
* 列表
*/
//判断有没有该权限,有的话可处理请求,没有的话会报错
@RequiresPermissions("/system/user/list")
@RequestMapping("/list")
public String list(String name, String phone, Page page, ModelMap map) {
QueryWrapper<User> queryWrapper = new QueryWrapper();
if (StrUtil.isNotBlank(name)) {
queryWrapper.like("u.name", name).or().like("phone", name);
}
if (StrUtil.isNotBlank(phone)) {
queryWrapper.like("phone", phone);
}
map.put("name", name);
map.put("phone", phone);
map.put("page", new MyPage(iUserService.pageVo(page, queryWrapper)));
return "system/user/list";
}
- 在前端页面引入shiro标签库并使用shiro标签判断权限,有权限的才能看到标签内的内容
<%@ taglib prefix="" uri="http://shiro.apache.org/tags" %>
<!-- name属性进行指定的权限判断,有权限才会显示标签内的内容 -->
<shiro:hasPermission name="/system/user/toInsert">
<a href="javascript:;"
onclick="insert('添加','${pageContext.request.contextPath}/system/user/toInsert','800','500')"
class="btn btn-primary radius"> <i class="Hui-iconfont"></i> 添加
</a>
</shiro:hasPermission>
- 使用shiro进行退出登陆
/**
* 注销
*
* @return
*/
@RequestMapping("/logout")
public String logout(HttpSession session) {
//使用shiro退出登录
SecurityUtils.getSubject().logout();
session.removeAttribute(Constant.SESSION_USER);
return "redirect:/";
}