1、shiro是什么
用官方的话说Apache Shiro是一个功能强大且易于使用的Java安全框架,它为开发人员提供了一种直观而全面的解决方案,用于身份验证,授权,加密和会话管理。
首先我们先看官方给出的Shiro外部结构图,看shiro是如何完成工作的:
shiro主要有三大功能模块:
Subject:
应用代码直接交互的对象Subject,也就是说Shiro的对外API核心就是Subject,代表了当前的用户,也不一定是一个具体的人,也当前应用交互的任何东西都是Subject,与Subject的所有交互都会委托给SecurityManager,Subject其实就是一个门面,SecurityManager才是实际的执行者
SecurityManager:
安全管理器,即所有与安全有关的操作都会与SecurityManager交互,并且它管理着所有的Sbject,它才是Shiro的核心,负责与Shiro的其他组件进行交互
Realms:
Shiro从Realm获取安全数据(用户,角色,权限),就是说SecurityManager要验证用户身份,它就需要从Realm获取相应的用户进行比较,来确定用户的身份是否合法,也需要用Realm得到用户相应的角色、权限,进行验证用户的操作是否能够进行
2、集成Shiro
1、导入jar包
在pom文件中添加shiro依赖
org.apache.shiro
shiro-spring
1.6.0
2、编写配置类
创建ShiroConfig类
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
//DefaultWebSecurityManager
//创建realm对象
}
这三个就对应我们刚刚说的三个模块,然后就开始配置这三个对象,我们先从realm开始配置,realm对象需要我们自己先定义,我们先新建一个ShiroRealm类然后继承 AuthorizingRealm,然后重写他的方法,一个授权,一个认证
//自定义realm 需要继承 AuthorizingRealm
public class ShiroRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
然后返回ShiroConfig去创建realm对象
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
//DefaultWebSecurityManager
//创建realm对象
@Bean
public ShiroRealm shiroRealm(){
return new ShiroRealm();
}
}
这样我们写的对象就被spring托管了,接下来我们写DefaultWebSecurityManager,因为DefaultWebSecurityManager需要Realm
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
//DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(shiroRealm);
return securityManager;
}
//创建realm对象
@Bean
public ShiroRealm shiroRealm(){
return new ShiroRealm();
}
}
接下来写ShiroFilterFactoryBean
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager manager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(manager);
return bean;
}
//DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(shiroRealm);
return securityManager;
}
//创建realm对象
@Bean
public ShiroRealm shiroRealm(){
return new ShiroRealm();
}
}
3、设置过滤器
前面我们已经把shiro初步集成了,接下来就需要配置过滤器
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager manager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(manager);
/*
添加shiro的内置过滤器
anon: 无需认证就可访问
authc: 必须认证才能访问
user: 必须拥有 记住我功能才可访问
perms: 拥有对某个资源的权限才能访问
role: 拥有某个角色权限才能访问
*/
MapfilterMap = new HashMap<>();
filterMap.put("/version/*","authc");
filterMap.put("/api/*","anon");
bean.setFilterChainDefinitionMap(filterMap);
//设置登录请求
bean.setLoginUrl("/home/toLogin");
return bean;
}
这样配置的意思是路径/version/是需要认证才可访问,/api/无需认证就可访问,然后没认证的返回登录页面去认证,接下来试试,在网页中输入网址
因为没认证过直接跳转到了登录页面,这样就算成功了
4、实现登录功能
现在我们已经成功拦截了我们设置的路径,然后就要实现登录通过认证,首先写一个用户实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Admin {
private Integer id;
//用户名称
private String name;
//登录帐号
private String username;
//登录密码
private String password;
//创建时间
private Date createTime;
}
然后写登录方法
@PostMapping("login")
public String login(String adminName, String password, Model model) {
Admin admin = adminMapper.searchByAccount(adminName);
if (admin == null){
model.addAttribute("error","帐号密码错误");
return "home/login";
}
if (!admin.getPassword().equals(password)){
model.addAttribute("error","帐号密码错误");
return "home/login";
}
//获取当前用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(admin.getUsername(),admin.getPassword());
try {
subject.login(token); //执行登录方法
return "redirect:/version/list";
} catch (Exception e){
e.printStackTrace();
}
return "home/login";
}
这样我们在请求登录请求后他会去执行doGetAuthenticationInfo方法做认证操作,那我们就去里面写认证操作
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String adminName = token.getUsername();
if (adminName != null && !adminName.equals("")){
Admin admin = adminMapper.searchByAccount(adminName);
if (admin != null){
return new SimpleAuthenticationInfo(admin.getUsername(),admin.getPassword(),getName());
}
}
return null;
}
如果查询数据库没有该用户return null后会抛出一个UnknownAccountException异常,成功返回AuthenticationInfo的实现类
这样我们就实现了登录认证功能