跟着【狂神说Java】SpringBoot最新教程IDEA版通俗易懂P40-P44在Springboot中简单整合了Shiro并实现了登录认证和访问授权功能。
pom依赖
<!-- Shiro整合Spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
ShiroConfig
package com.zzw.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
// ShiroConfig里面需要创建3大对象:
// ShiroFilterFactoryBean
// DefaultWebSecurityManager
// Realm
// 1. 创建Realm
// 这个类主要用来权限认证
@Bean
public UserRealm userRealm() {
return new UserRealm();
}
// 2. 创建DefaultWebSecurityManager,并关联realm
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager webSecurityManager = new DefaultWebSecurityManager();
// 关联realm:这里要注意,我们的Realm被创建并被注入到了Spring容器中,这里要获取必须从方法参数中获取,而方法参数必须使用@Qualifier注解
webSecurityManager.setRealm(userRealm);
return webSecurityManager;
}
// 3. 创建ShiroFilterFactoryBean
// 这个类主要用来过滤请求
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 关联DefaultWebSecurityManager,即设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// 添加shiro内置的过滤器
/*
anon: 无需认证就可以访问
authc: 必须认证了才可以访问
user: 必须拥有 记得我 功能才可以访问
perms: 必须拥有对某个资源的权限才可以访问
role: 必须拥有某个角色权限才可以访问
*/
// 请求的过滤通常在这里设置,真实项目可能有几百条
Map<String, String> filterMap = new LinkedHashMap<>();
// 这里要注意put的顺序,测试时发现perms和authc的顺序不正确可能造成bug
// 对特定资源进行权限控制
filterMap.put("/user/add", "perms[user:add]");
filterMap.put("/user/update", "perms[user:update]");
// 把特定资源放入filterMap
filterMap.put("/user/*", "authc");
// 把过滤规则的map放入factory
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
// 设置登录的请求
shiroFilterFactoryBean.setLoginUrl("/toLogin");
// 设置未授权的页面
shiroFilterFactoryBean.setUnauthorizedUrl("/unautho");
return shiroFilterFactoryBean;
}
}
UserRealm
这个类里重写授权和认证方法
package com.zzw.config;
import com.zzw.pojo.User;
import com.zzw.service.UserService;
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.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了->授权");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Subject currentUser = SecurityUtils.getSubject();
User user = (User) currentUser.getPrincipal();
info.addStringPermission(user.getPerms());
return info;
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了->认证");
// 验证登录的逻辑写在这里
// 连接真实的数据库
// 方法参数里有一个token,我们需要验证这个token:
// 1. 强转一下这个token
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)authenticationToken;
User user = userService.queryUserByName(usernamePasswordToken.getUsername());
if (user == null) {
// 没有这个人
return null; // 代表没有这个人
}
// 密码认证,交由shiro来做
// 目前我们把密码未经加密直接放到db中,所以取出来的密码就是未加密的密码
// 未来可以在注册时,将密码加密后再存到db中
// 权限:我们把每个用户的权限放在数据库中,每次认证时,把service层读取到的user,放到principal中,这样授权方法中可以获取了
return new SimpleAuthenticationInfo(user, user.getPwd(), "");
}
}
UserController
package com.zzw.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
// 在登录的时候,注册token
@RequestMapping("/login")
public String login(String username, String password, Model model) {
// 获取当前用户
Subject currentUser = SecurityUtils.getSubject();
// 封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
System.out.println(token.getPassword());
try {
currentUser.login(token); // 执行登录方法,如果这个方法没有异常就代表登录OK
return "index";
} catch (UnknownAccountException e) { // 账号错误
model.addAttribute("msg", "用户名不存在");
return "login";
} catch (IncorrectCredentialsException e) { // 密码错误
model.addAttribute("msg", "密码错误");
return "login";
}
}
// 未授权页面
@RequestMapping("/unautho")
@ResponseBody
public String unautho() {
return "未经授权,不许访问";
}
}