写在前边:Shiro是非常强大的安全框架,与Spring security相比,它更容易上手。
公众号:小白编码
Shiro介绍:
Apache Shiro 是 Java 的一个安全(权限)框架
Subject
:正如我们在刚才提到教程,在Subject本质上是当前正在执行的用户的安全特定“视图”。“用户”一词通常表示一个人,一个人Subject可以是一个人,但它也可以表示第三方服务,守护程序帐户,cron作业或类似的东西-基本上是当前与该软件交互的任何东西。
Subject实例都绑定到(并要求)SecurityManager。当您与互动时Subject,这些互动会转化为与主题相关的互动SecurityManager。
SecurityManager
:SecurityManager是Shiro体系结构的核心,并充当一种“伞”对象,可协调其内部安全组件,这些组件一起形成对象图。但是,一旦为应用程序配置了SecurityManager及其内部对象图,通常就不理会它,并且应用程序开发人员几乎所有的时间都花在SubjectAPI上。
Realms
:领域充当Shiro与应用程序的安全数据之间的“桥梁”或“连接器”。当真正需要与安全性相关的数据(例如用户帐户)进行交互以执行身份验证(登录)和授权(访问控制)时,Shiro会从一个或多个为应用程序配置的领域中查找许多此类内容。
组件流程:
Shiro的架构:
搭建环境
Springboot+mybatis+mysql+shiro
user表:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`password` varchar(50) NOT NULL,
`nick_name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
user类:
@Data
public class User {
private Integer id;
private String username;
private String password;
private String nickName;
}
UserMapper:
public interface UserMapper {
@Select("select * from user where username = #{username}")
User findUserByUserName(String username);
}
UserService:
public interface UserService {
/**
* 根据用户名查询用户信息
* @param username
* @return
*/
User findUserByUserName(String username);
}
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User findUserByUserName(String username) {
return userMapper.findUserByUserName(username);
}
}
Shiro配置UserRealm:
public class UserRealm extends AuthorizingRealm {
private static final Logger LOGGER = LoggerFactory.getLogger(UserRealm.class);
@Autowired
private UserService userService;
/**
* 授权逻辑
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑!");
return null;
}
/**
* 认证逻辑
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑!");
//当前登陆的token信息
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//获取用户:使用token判断用户名
User user = userService.findUserByUserName(token.getUsername());
//如果用户不存在:
if (user == null) {
LOGGER.info("用户不存在{}");
//返回null,底层抛出异常:UnKnowAccountException
return null;
}
//2.判断密码 : user会传到Subject中
return new SimpleAuthenticationInfo(user, user.getPassword(), "");
}
}
ShiroConfig:
@Configuration
public class ShiroConfig {
/**
* 安全过滤器
* @param defaultWebSecurityManager 安全管理器
* @return 返回Shiro过滤器工厂
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//设置登陆的url
shiroFilterFactoryBean.setLoginUrl("/login");
return shiroFilterFactoryBean;
}
/**
* 默认安全管理器
* @param userRealm 关联的自定义Realm
* @return 默认安全管理器
*/
@Bean("securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
//关联realm
manager.setRealm(userRealm);
return manager;
}
/**
* 创建Realm(Shiro连接数据的桥梁)
* @return 返回一个Realm
*/
@Bean("userRealm")
public UserRealm gerUserRealm() {
return new UserRealm();
}
}
执行登陆功能:
/**
* 登陆逻辑
* @param user
* @return
*/
@PostMapping("/login")
@ResponseBody
public Result login(User user) {
if (user != null) {
//1. 获取subject 用户主体
Subject subject = SecurityUtils.getSubject();
//2. 封装用户数据Token登陆
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
//3.执行登陆方法
subject.login(token);
//没有异常抛出就是登陆成功!
return Result.success("登陆成功!");
} catch (UnknownAccountException e) {
//UnknownAccountException异常:用户名不存在
return Result.failure("用户不存在!");
} catch (IncorrectCredentialsException e) {
//IncorrectCredentialsException异常:密码错误
return Result.failure("密码错误!");
}
}
//如果参数为空
return Result.failure("登陆参数不能为空!");
}
结果:
{
"code": 200,
"message": "处理成功!",
"data": "登陆成功!"
}
执行登出功能:
/**
* 退出登陆
* @return
*/
@GetMapping("/logout")
@ResponseBody
public Result logout() {
//获取当前主体
Subject subject = SecurityUtils.getSubject();
//如果已登录
if (subject.isAuthenticated()) {
//退出登陆
subject.logout();
return Result.success("退出成功!");
}
return Result.failure("退出失败!");
}
执行结果:
{
"code": 200,
"message": "处理成功!",
"data": "退出成功!"
}