导入依赖
org.springframework.boot
spring-boot-starter-web
org.apache.shiro
shiro-spring-boot-web-starter
1.12.0
配置类
package com.qf.shiro2302.config;
import com.qf.shiro2302.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.List;
@Configuration
@Slf4j
public class ShiroConfig {
@Bean
public Realm realm(){
AuthorizingRealm authorizingRealm = new AuthorizingRealm() {
/**
*
* @param token 这的token就是调用login方法时,传入的token对象
* @return AuthenticationInfo 对象中,封装了用户的身份信息(principals),密码(credentials),提供信息的realm的名字
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("===========获取身份信息===============");
//查询数据库获取当前用户名对应的User对象
String username = (String) token.getPrincipal();
System.out.println("username="+username);
System.out.println("this.getName()={}"+this.getName());
User user=getUserFromDB(username);
//需要返回shiro规定的AuthenticationInfo类型的对象
//这个对象中,包含了用户的身份认证信息
// SimpleAuthenticationInfo的构造函数中的第一个参数,principals代表用户的身份信息
// 一般可以使用 user对象,或者使用用户名也可以
// 第三个参数,代表当前realm的名字,固定写法
//Authentication 证明的意思
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
return authenticationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("============获取授权===============");
// 从数据库表中查询当前用户具有的角色和权限字符串
User user = (User) principalCollection.getPrimaryPrincipal();
List roles= getRolesFromDB(user);
List permissions= getPermissionFromDB(user);
// 按照约定返回AuthorizationInfo对象
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 放入从数据库中查询到的当前用户的角色信息
// authorizationInfo.addRole("test");
authorizationInfo.addRoles(roles);
// 放入从数据库中查询到的当前用户的权限信息
// authorizationInfo.addStringPermission("document:read");
authorizationInfo.addStringPermissions(permissions);
return authorizationInfo;
}
};
return authorizingRealm;
}
private List getRolesFromDB(User user) {
return Arrays.asList("test","admin");
}
private List getPermissionFromDB(User user) {
return Arrays.asList("document:read","document:write");
}
private User getUserFromDB(String username) {
User user = new User(100, "数据库用户", "123456", "[email protected]");
return user;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
//ShiroFilterChainDefinition 此接口就一个实现类 默认Shiro过滤器链定义类
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
// 让登录接口直接被shiro的过滤器放行
//第一个参数:接口的路径
//第二个参数:shiro内部的过滤器的名字,anon代表无需登录即可访问的特殊的过滤器,注意过滤器的名字是固定的,不能乱写
chainDefinition.addPathDefinition("/login/dologin", "anon");
// 放行springboot的错误页面的请求url 这个页面就是个response拼接的页面
chainDefinition.addPathDefinition("/error", "anon");
//增加角色或者权限,对于某些请求
// logged in users with the 'test' role
// chainDefinition.addPathDefinition("/test/**", "authc, roles[test,admin], perms[document:read,document:write]"); 这个如果改成anon,就不能加后面的角色或者权限,否则,将会被认为需要登录,重定向到登录页
// all other paths require a logged in user
chainDefinition.addPathDefinition("/**", "authc");
return chainDefinition;
}
}
调用接口
package com.qf.shiro2302.controller;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/login")
@Slf4j
public class LoginController {
@PostMapping("/dologin")
public String dologin(String username,String password){
//使用Shiro进行登录处理
Subject subject = SecurityUtils.getSubject();//获取shiro核心对象
//为了调用shiro的登录方法,需要准备一个Token对象
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
System.out.println(token);
subject.login(token);//使用shiro登录流程
return "登陆成功 ";
}
}
获取ShiroSession中的用户,注解添加权限
package com.qf.shiro2302.controller;
import com.qf.shiro2302.entity.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class TestController {
@RequiresRoles({"test","admin"})
@RequiresPermissions({"document:read","document:write"})
@RequestMapping("/test1")
public String hello1(){
return "hello shiro !!!";
}
@RequestMapping("/test2")
public User hello2(){
//如果使用shiro获取当前登录用户的身份信息
Subject subject = SecurityUtils.getSubject();
User principal = (User) subject.getPrincipal();
System.out.println(principal);
return principal;
}
}
shiro:
loginUrl: /login.html
#配置没登陆的时候重定向的页面。这个请求是可以放行的
1.配置类
package com.qf.shiroHomework.config;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qf.shiroHomework.entity.*;
import com.qf.shiroHomework.mapper.TPersMapper;
import com.qf.shiroHomework.mapper.TRoleMapper;
import com.qf.shiroHomework.mapper.TRolePermsMapper;
import com.qf.shiroHomework.mapper.TUserRoleMapper;
import com.qf.shiroHomework.service.ITUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@Configuration
@Slf4j
public class ShiroConfig {
@Autowired
private ITUserService itUserService;
@Autowired
private TUserRoleMapper tUserRoleMapper;
@Autowired
private TRolePermsMapper tRolePermsMapper;
@Autowired
private TPersMapper tPersMapper;
@Autowired
private TRoleMapper tRoleMapper;
//将Realm对象放入IOC容器里
@Bean
public Realm realm(){
AuthorizingRealm authorizingRealm = new AuthorizingRealm() {
/**
*
* @param token 这的token就是调用login方法时,传入的token对象
* @return AuthenticationInfo 对象中,封装了用户的身份信息(principals),密码(credentials),提供信息的realm的名字
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("===========获取身份信息===============");
//获取身份信息
String username = (String) token.getPrincipal();
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.eq("username",username);
TUser user = itUserService.getOne(wrapper);
// 需要返回shiro规定的AuthenticationInfo类型的对象
// 这个对象中,包含了用户的身份认证信息
// SimpleAuthenticationInfo的构造函数中的第一个参数,principals代表用户的身份信息
// 一般可以使用 user对象,或者使用用户名也可以
// 第三个参数: 盐
// 第四个参数,代表当前realm的名字,就是一个标识,标识是这个Bean调用的这个方法,没有作用,固定写法
SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user,user.getPassword(), ByteSource.Util.bytes(user.getSalt()),this.getName());
return authenticationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
TUser user = (TUser) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
log.info("用户角色={}",getRolesFromDB(user));
log.info("用户权限={}",getPermissionFromDB(user));
authorizationInfo.addRoles(getRolesFromDB(user));
authorizationInfo.addStringPermissions(getPermissionFromDB(user));
return authorizationInfo;
}
};
// 把HashedCredentialsMatcher对象设置到authorizingRealm对象中
authorizingRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return authorizingRealm;
}
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
//设置算法名称
matcher.setHashAlgorithmName("md5");
//设置hash次数
matcher.setHashIterations(1024);
return matcher;
}
//配置Shiro过滤器链
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
chainDefinition.addPathDefinition("/register.html","anon");
chainDefinition.addPathDefinition("/error/500.html","anon");
chainDefinition.addPathDefinition("/error","anon");
chainDefinition.addPathDefinition("/user/register","anon");
chainDefinition.addPathDefinition("/user/login","anon");
chainDefinition.addPathDefinition("/user/**","authc");
chainDefinition.addPathDefinition("/**","authc");
return chainDefinition;
}
public List getPermissionFromDB(TUser user) {
if (user.getPerms()!=null){
return user.getPerms();
}
Integer id = user.getId();
//通过用户ID找到角色
QueryWrapper wrapper1 = new QueryWrapper<>();
wrapper1.eq("userid",id);
TUserRole tUserRole = tUserRoleMapper.selectOne(wrapper1);
Integer roleid = tUserRole.getRoleid();
System.out.println(roleid);
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.select("permsid").eq("roleid",roleid);
List
实体类
package com.qf.shiroHomework.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.List;
import lombok.*;
/**
*
*
*
*
* @author jmj
* @since 2023-08-10
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@TableName("t_user")
public class TUser implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String username;
private String password;
private String salt;
@TableField(exist = false) // 说明当前这个属性在数据库表中没有对应的字段,让mp生成sql时忽略这个属性
private List roleName;
@TableField(exist = false)
private List perms;
}
Controller
//复杂密码匹配器
@PostMapping("/login")
public String login(TUser user){
//使用Shiro进行登录处理
Subject subject = SecurityUtils.getSubject();//获取shiro核心对象
//为了调用shiro的登录方法,需要准备一个Token对象
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(), user.getPassword());
subject.login(usernamePasswordToken);
return "redirect:/show.html";
}
以前手写MD5处理的方法
// 简单密码匹配器方案
// @PostMapping("/login")
// public String login(TUser user){
// //使用Shiro进行登录处理
// Subject subject = SecurityUtils.getSubject();//获取shiro核心对象
// //为了调用shiro的登录方法,需要准备一个Token对象
// //添加Where 条件
// QueryWrapper wrapper = new QueryWrapper<>();
// wrapper.eq("username",user.getUsername());
// TUser u = itUserService.getOne(wrapper);
// if (u==null){
// return "redirect:/index.html";
// }else {
//
// //MD5加密
// Md5Hash md5Hash = new Md5Hash(user.getPassword(), u.getSalt(), 1024);
// String newPassword = md5Hash.toHex();
//
//
// UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(), newPassword);
//
// subject.login(usernamePasswordToken);
// }
//
// return "redirect:/show.html";
//
// }