Shiro
是一款主流的 Java 安全框架,不依赖任何容器,可以运行在 Java SE 和 Java EE 项目中,它的主要作用是对访问系统的用户进行身份认证、授权、会话管理、加密等操作。
Shiro 就是用来解决安全管理的系统化框架。
核心组件
用户、角色、权限
会给角色赋予权限,给用户赋予角色
1、UsernamePasswordToken,Shiro 用来封装用户登录信息,使用用户的登录信息来创建令牌 Token。
2、SecurityManager,Shiro 的核心部分,负责安全认证和授权。
3、Suject,Shiro 的一个抽象概念,包含了用户信息。
4、Realm,开发者自定义的模块,根据项目的需求,验证和授权的逻辑全部写在 Realm 中。
5、AuthenticationInfo,用户的角色信息集合,认证时使用。
6、AuthorzationInfo,角色的权限信息集合,授权时使用。
7、DefaultWebSecurityManager,安全管理器,开发者自定义的 Realm 需要注入到 DefaultWebSecurityManager 进行管理才能生效。
8、ShiroFilterFactoryBean,过滤器工厂,Shiro 的基本运行机制是开发者定制规则,Shiro 去执行,具体的执行操作就是由 ShiroFilterFactoryBean 创建的一个个 Filter 对象来完成。
大致的运行机制
subject:主体,可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证、授权。
securityManager:安全管理器,主体进行认证和授权都是通过securityManager进行。
authenticator:认证器,主体进行认证最终通过authenticator进行的。
authorizer:授权器,主体进行授权最终通过authorizer进行的。
Realm:可以有1个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的。它可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等。
SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 需要有人去管理它的生命周期,这个组件就是 SessionManager。而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境
编写认证和授权规则:
认证过滤器
anon:无需认证。
authc:必须认证。
authcBasic:需要通过 HTTPBasic 认证。
user:不一定通过认证,只要曾经被 Shiro 记录即可,比如:记住我。
授权过滤器
perms:必须拥有某个权限才能访问。
role:必须拥有某个角色才能访问。
port:请求的端口必须是指定值才可以。
rest:请求必须基于 RESTful,POST、PUT、GET、DELETE。
ssl:必须是安全的 URL 请求,协议 HTTPS。
项目创建
所需要的依赖
entity
@Data
public class Account {
private Integer id;
private String username;
private String password;
private String perms;
private String role;
}
mapper
@Repository
public interface AccountMapper extends BaseMapper {
controller
@Controller
public class AccountController {
@GetMapping("/{url}")
public String redirect(@PathVariable("url") String url){
return url;
}
// 传过来的数据封装到token 用subject调用 逻辑交给shiro管理
@PostMapping("/login")
public String login(String username, String password, Model model){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try {
subject.login(token);
Account account = (Account) subject.getPrincipal();
subject.getSession().setAttribute("account",account);
return "index";
} catch (UnknownAccountException e) {
e.printStackTrace();
model.addAttribute("msg","用户名错误!");
return "login";
} catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码错误!");
e.printStackTrace();
return "login";
}
}
@GetMapping("/unauth")
@ResponseBody
public String unauth(){
return "未授权,无法访问!";
}
@GetMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "login";
}
}
config
@Configuration
//注解是生效配置类 去掉注释 配置类不在生效
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
//权限设置
Map map = new Hashtable<>();
map.put("/main","authc");
map.put("/manage","perms[manage]");
map.put("/administrator","roles[administrator]");
factoryBean.setFilterChainDefinitionMap(map);
//设置登录页面
factoryBean.setLoginUrl("/login");
//设置未授权页面
factoryBean.setUnauthorizedUrl("/unauth");
return factoryBean;
}
@Bean
//ioc容器中拿到accoutealm 依据名字进行查询 然后注入到manager
public DefaultWebSecurityManager securityManager(@Qualifier("accoutRealm") AccoutRealm accoutRealm){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(accoutRealm);
return manager;
}
@Bean
//放到ioc容器中
public AccoutRealm accoutRealm(){
return new AccoutRealm();
}
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
AccoutRealm
public class AccoutRealm extends AuthorizingRealm {
@Autowired
private AccountService accountService;
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取当前登录的用户信息
Subject subject = SecurityUtils.getSubject();
Account account = (Account) subject.getPrincipal();
//设置角色
Set roles = new HashSet<>();
roles.add(account.getRole());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
//设置权限
info.addStringPermission(account.getPerms());
return info;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
//先认证然后在进行的权限的判定
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 生成token 这个是用户在前端输入的用户名和密码
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 在token中拿到用户输入的用书名于数据库中用户进行对比有的话执行没有抛出一样
Account account = accountService.findByUsername(token.getUsername());
if(account != null){
// 验证密码 数据库中查出密码和传过来的密码进行对比 下面对象会自动的将token中的密码和正确的密码对比验证
return new SimpleAuthenticationInfo(account,account.getPassword(),getName());
}
return null;
}
}
要在启动类加上mapper注解扫描
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
org.apache.shiro
shiro-spring
1.5.3
mysql
mysql-connector-java
com.baomidou
mybatis-plus-boot-starter
3.3.1.tmp
com.github.theborakompanioni
thymeleaf-extras-shiro
2.0.0
静态页面
Title
index
main
login
Title
main
Title
main
manage
Title
manage
administrator
Title
administator
用户提交登录 数据传到controller
查询到数据库中没有用户输出的用户名