spring boot简单搭建一个Shiro项目demo
前言
搭建一个shiro的项目demo,体验shiro的权限控制功能以及数据库表的简单设计。
使用步骤
1.数据库表的设计
主要分为三张主要表和两张关联表,分别是:
1)三张主表:用户表(user)、角色表(user)和权限表(permission)
2)两张关联表:用户角色表(user_role)和角色权限表(permission_role)
/**
* ①用户表包括:id、用户名和密码
* ②角色表包括:id和角色名
* ③权限表包括:id、权限名和路径
* ④用户角色关联表包括:用户id和角色id
* ⑤权限角色关联表包括:角色id和权限id
*/
/**
*对应实体类的设计
*/
@Data
public class User {
private Integer uid;
private String username;
private String password;
private Set<Role> roles = new HashSet<>();
}
@Data
public class Role {
private Integer rid;
private String rname;
private Set<User> users = new HashSet<>();
private Set<Permission> permissions = new HashSet<>();
}
@Data
public class Permission {
private Integer pid;
private String pname;
private String url;
}
2.导入所需的依赖以及对应的配置
maven依赖文件如下:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
yml配置文件如下:
spring:
#database-mysql
datasource:
url: jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#mybatis
mybatis:
#实体类所放置的包位置
type-aliases-package: com.shiro.model
#映射文件所放置的文件位置
mapper-locations: mappers/*.xml
3.创建配置类
(1)创建身份认证类AuthRealm.class
/**
* Created by Jzs on 2021/6/27
* 身份认证与权限分配
*/
public class AuthRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//认证登录
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
String username = usernamePasswordToken.getUsername();
User user = userService.findByUsername(username);
return new SimpleAuthenticationInfo(user,user.getPassword(),this.getClass().getName());
}
//用户授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//登录成功之后会把登录成功的用户对象放入session中
User user = (User) principals.fromRealm(this.getClass().getName()).iterator().next();
List<String> permissionList = new ArrayList<>();
List<String> roleNameList = new ArrayList<>();
//获取用户所有的角色
Set<Role> roles = user.getRoles();
if(CollectionUtils.isNotEmpty(roles)){
//角色不为空就进行遍历获取角色的名字和权限
roles.stream().forEach(r->{
//设置角色名称
roleNameList.add(r.getName());
//设置权限
Set<Permission> permissions = r.getPermissions();
if (CollectionUtils.isNotEmpty(permissions)){
List<String> collect = permissions.stream().map(p -> p.getName()).collect(Collectors.toList());
permissionList.addAll(collect);
}
});
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//将权限和角色名称分别放入SimpleAuthorizationInfo,shiro中进行过滤的时候才能根据角色和权限进行分配
info.addStringPermissions(permissionList);
info.addRoles(roleNameList);
return info;
}
}
此处用一个userService,其中的根据用户名获取用户信息的代码也粘贴一下,对实体类的嵌套查询还是
(2)创建密码校验类CredentialMatcher.class
/**
* Created by Jzs on 2021/6/27
* 自定义的密码校验规则,此处只是单纯的进行字符串对比
* 可以使用自定义的密码加密和校验规则,如MD5等
*/
public class CredentialMatcher extends SimpleCredentialsMatcher {
/**
* @Description:
* @Author: Jzs
* @Date: 2021/6/27 22:19
* @param token: 传入的用户信息
* @param info: 数据库中查询出的用户信息
* @return: boolean
**/
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//传入的信息获取到的密码,返回的是char型数组,需要转换成String类型
String password = new String(usernamePasswordToken.getPassword());
//从数据库查询到的数据的密码
String dbPassword = (String) info.getCredentials();
return this.equals(password,dbPassword);
}
}
(3)创建shiro配置类ShiroConfiguration.class
/**
* Created by Jzs on 2021/6/27
* Shiro配置类
*/
@Configuration
public class ShiroConfiguration {
//DefaultFilter
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
//配置登录、登录成功、失败等跳转接口
bean.setLoginUrl("/login");
bean.setSuccessUrl("/index");
bean.setUnauthorizedUrl("/unauthorized");
//配置权限规则
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//index的路径使用authc的权限认证(DefaultFilter,每一个都是一个拦截类)
filterChainDefinitionMap.put("/index","authc");//需要身份认证
filterChainDefinitionMap.put("/","authc");//需要身份认证
filterChainDefinitionMap.put("/login","anon");//忽略
filterChainDefinitionMap.put("/loginUser","anon");//忽略
//RolesAuthorizationFilter,具有这个角色的用户才可以访问
filterChainDefinitionMap.put("/search","roles[user]");
//PermissionsAuthorizationFilter,具有这个权限的用户才可以访问
filterChainDefinitionMap.put("/edit","perms[edit]");
//filterChainDefinitionMap.put("/druid/**","anon");//将druid的请求全部放开
filterChainDefinitionMap.put("/**","user");//当前是否登录用户
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
@Bean("securityManager")
public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(authRealm);
return manager;
}
//将自定义的密码校验器设置到authRealm中
@Bean("authRealm")
public AuthRealm authRealm(@Qualifier("credentialMatcher") CredentialMatcher credentialMatcher){
AuthRealm authRealm = new AuthRealm();
authRealm.setCredentialsMatcher(credentialMatcher);
return authRealm;
}
//注入自定义的密码校验器
@Bean("credentialMatcher")
public CredentialMatcher credentialMatcher(){
return new CredentialMatcher();
}
//shiro跟spring关联的配置
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(@Qualifier("securityManager") SecurityManager securityManager){
//使用代理,默认是false=》true
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
4.测试controller
@Controller
public class TestController {
//跳转到登录界面
@GetMapping("/login")
public String login(){
return "login";
}
//跳转到首页
@GetMapping(value = {"/index","/"})
public String index(){
System.out.println("======");
return "index";
}
//用户登录请求
@PostMapping("/loginUser")
public String loginUser(@RequestParam("username") String username,
@RequestParam("password") String password,
HttpSession session){
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
User user = (User) subject.getPrincipal();
session.setAttribute("user",user);
return "index";
}catch (Exception e){
return "login";
}
}
//没有权限跳转接口
@GetMapping("/unauthorized")
public String unauthorized(){
System.out.println("unauthorized====");
return "unauthorized";
}
//用户注销,跳转到登录界面
@GetMapping("/logout")
public String logout(){
System.out.println("logout====");
Subject subject = SecurityUtils.getSubject();
if(!Objects.isNull(subject)){
//如果存在认证信息,进行注销
subject.logout();
}
return "logout";
}
//搜索接口
@GetMapping("/search")
@ResponseBody
public String search(){
System.out.println("search====");
return "search====";
}
//编辑接口
@GetMapping("/edit")
@ResponseBody
public String edit(){
System.out.println("edit====");
return "edit====";
}
}