Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
例子
这篇文章主要实现以下这么个例子:
index页面中有三个超链接,分别对应add、login、update页面,我们需要完成以下需求
添加依赖
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-spring-boot-web-starterartifactId>
<version>1.8.0version>
dependency>
创建shiro配置类
@Configuration
public class ShiroConfig {
//创建realm对象,自定义类继承AuthorizingRealm
@Bean
public UserRealm getRealm(){
return new UserRealm();
}
//配置defaultSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("getRealm") Realm realm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//关联realm
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
//配置shiro过滤器(设置拦截写在这里)
@Bean(name = "shiroFilterFactoryBean") //必须设置name为shiroFilterFactoryBean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
return bean;
}
}
//设置授权、认证的操作写在这里
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
//配置shiro过滤器
@Bean(name = "shiroFilterFactoryBean") //必须设置name为shiroFilterFactoryBean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
/**登录拦截
* anon:无需认证就可以访问
* authc:必须认证了才能访问
* user:必须拥有 记住我 功能才能使用
* perms:拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
*/
Map<String, String> filterChainDefinitionMap = new HashMap<>();
//认证(可以使用通配符,也可以一一设置)
filterChainDefinitionMap.put("/user/*","authc");
// filterChainDefinitionMap.put("/user/add","authc");
// filterChainDefinitionMap.put("/user/update","authc");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//设置跳转登录页,认证失败的话会跳转到登录页
bean.setLoginUrl("/user/login");
return bean;
}
用户认证就等同于用户登录
@Slf4j
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.info("----调用认证接口----");
//authenticationToken存放着用户登录信息
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
User user = userService.getOne(new QueryWrapper<User>().eq("username",userToken.getUsername()));
if (user == null){
return null;
}
//密码认证,shiro自己做,第二个参数将用户真实密码传进来,shiro会自动进行比对
return new SimpleAuthenticationInfo("",user.getPassword(),"");
}
}
controller层
@PostMapping("/login")
public String login(String username,String password,Model model){
//获取用户数据
Subject currentUser = SecurityUtils.getSubject();
//封装用户登录信息
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
//将用户的登录信息进行认证
currentUser.login( token );
return "index";
} catch ( UnknownAccountException uae ) {
//用户没有在系统中
model.addAttribute("loginMsg","用户不存在");
return "user/login";
} catch ( IncorrectCredentialsException ice ) {
//密码错误
model.addAttribute("loginMsg","密码错误");
return "user/login";
}
}
首先我们需要添加过滤器,设置用户必须有哪些权限才能访问页面
//配置shiro过滤器
@Bean(name = "shiroFilterFactoryBean") //必须设置name为shiroFilterFactoryBean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
/**登录拦截
* anon:无需认证就可以访问
* authc:必须认证了才能访问
* user:必须拥有 记住我 功能才能使用
* perms:拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
*/
Map<String, String> filterChainDefinitionMap = new HashMap<>();
//授权(设置add页面必须有user:add权限,update页面必须有user:update权限)
filterChainDefinitionMap.put("/user/add","perms[user:add]");
filterChainDefinitionMap.put("/user/update","perms[user:update]");
//认证
filterChainDefinitionMap.put("/user/*","authc");
// filterChainDefinitionMap.put("/user/add","authc");
// filterChainDefinitionMap.put("/user/update","authc");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//设置跳转登录页
bean.setLoginUrl("/user/login");
//跳转到未授权页面
bean.setUnauthorizedUrl("/noauth");
return bean;
}
完成以上操作我们发现无论谁登录都直接被拦截了,这时候我们要设置哪些用户可以拥有权限
@Slf4j
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.info("----调用授权接口----");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//获取subject
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal();
//user.getPerms(),数据库有设置对应的值为user:add或其他
info.addStringPermission(user.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.info("----调用认证接口----");
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
User user = userService.getOne(new QueryWrapper<User>().eq("username",userToken.getUsername()));
if (user == null){
return null;
}
//密码认证,shiro自己做,将符合登录条件的user查出来放到第一个参数,就可以将user传入到subject中
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
以上大部分需求我们都已经完成了,还剩最后一个需求,要求没有权限的用户就不显示该按钮,这里我们需要整合thymeleaf
引入依赖
<dependency>
<groupId>com.github.theborakompanionigroupId>
<artifactId>thymeleaf-extras-shiroartifactId>
<version>2.1.0version>
dependency>
修改shiro配置文件
//整合thymeleaf
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
前端页面的使用
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thmeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<h1>首页h1>
<p th:text="${msg}">p>
<hr>
<div th:if="${session.loginUser==null}">
<a th:href="@{/user/login}">登录a>
div>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">adda>
div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">updatea>
div>
<div th:if="${session.loginUser!=null}">
<a th:href="@{/user/logout}">登出a>
div>
body>
html>