Apache Shiro是Java的一个安全框架,旨在简化身份验证和授权。Shiro在JavaSE和JavaEE项目中都可以使用。它主要用来处理身份认证,授权,企业会话管理和加密等。
核心主键:
UsernamePasswordToken
SecurityManager 负责安全认证和授权
Subject 访问系统的用户,主体
Realm 需要继承 AuthorizingRealm 类
AuthenticationInfo 用户认证
AuthorizationInfo 用户授权
DefaultWebSecurityManager 安全管理器自定义realm需要注入到进来
ShiroFilterFactoryBean 过滤器工厂
Principal:身份信息
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.6.0</version>
</dependency>
realm
public class AccountRealm extends AuthorizingRealm {
@Autowired
private AccountService accountService;
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* 认证
* 登录
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取用户携带的token,客户端将user信息封装到token
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
//或者
// String username = (String) authenticationToken.getPrincipal();
//用户名验证,是否存在
Account account = accountService.findByUsername(token.getUsername());
if(account!=null){
//shiro密码验证,是否正确
return new SimpleAuthenticationInfo(account,account.getPassword(),getName());
//shiro密码验证,有盐,是否正确
// return new SimpleAuthenticationInfo(account,account.getPassword(), ByteSource.Util.bytes(account.getSalt()),getName());
}
return null;
}
}
编写认证和授权规则
认证过滤器
anon:无需认证
authc:必须认证
user: 不一定要通过认证,曾经被shiro记录,如记住我
authcBasic:需要HTTPBasic认证
授权过滤器
perms:必须拥有某个权限才能访问
role: 必须拥有某个角色才能访问
port: 请求端口必须未指定值
rest:请求必须基于RESTful格式
ssl:必须是安全的RUL请求,HTTPS请求
/****************************************************/
创建3个页面
main.html、manage.html、admin.html
访问权限
1、必须登录才能访问mian
2、当前用户必须拥有manage权限授权才能访问manage。html
3、当前用户必须拥有admin角色才能访问admin.html
@Controller
public class AccountController {
@GetMapping("/{url}")
public String redirect(@PathVariable("url")String url){
return url;
}
/**
* 登录
* form表单提交不能用restful
*
*/
@PostMapping("login")
public String login(String username, String password, Model model){
//subject与shiro进行交互
Subject subject = SecurityUtils.getSubject();
//token封装用户信息
UsernamePasswordToken toekn = new UsernamePasswordToken(username, password);
//shiro开始登录验证
try {
subject.login(toekn);
//principal登录时存入的用户信息,可直接获取
Account account = (Account)subject.getPrincipal();
subject.getSession().setAttribute("account", account);
return "index";
} catch (UnknownAccountException e) {
model.addAttribute("msg","用户不存在");
return "login";
}catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码不正确");
return "login";
}
}
@GetMapping("/unauth")
@ResponseBody
public String unauth(){
return "权限不足";
}
@GetMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "login";
}
}
public class AccountRealm extends AuthorizingRealm {
@Autowired
private AccountService accountService;
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取当前用户信息
Subject subject = SecurityUtils.getSubject();
//principal登录时存入的用户信息,可直接获取
Account account = (Account)subject.getPrincipal();
//设置角色
Set<String> roles = new HashSet<>();
roles.add(account.getRole());
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(roles);
//设置权限
authorizationInfo.addStringPermission(account.getPerms());
return authorizationInfo;
}
/**
* 认证
* 登录
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取用户携带的token,客户端将user信息封装到token
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
//用户名验证,是否存在
Account account = accountService.findByUsername(token.getUsername());
//如果 token 未封装user
// String username = (String) authenticationToken.getPrincipal();
// Account account = accountService.findByUsername(username);
if(account!=null){
//shiro密码验证,是否正确
//account登录成功存入Subject中Object principal
return new SimpleAuthenticationInfo(account,account.getPassword(),getName());
//shiro密码验证,有盐,是否正确
// return new SimpleAuthenticationInfo(account,account.getPassword(), ByteSource.Util.bytes(account.getSalt()),getName());
}
return null;
}
}
/**
* 过滤器工厂
* @Qualifier("securityManager") 将securityManager从spring中取出
* @Bean(“shiroFilterFactoryBean”)可加可不加
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
//权限设置
Map<String,String> map = new HashMap<>();
map.put("/main", "authc");// authc 必须登录
map.put("/manage", "perms[manage]");// perms 必须有manage权限
map.put("/admin", "roles[admin]");// roles 必须有admin角色
factoryBean.setFilterChainDefinitionMap(map);
//设置登录页面
factoryBean.setLoginUrl("/login");
//设置未授权页面
factoryBean.setUnauthorizedUrl("/unauth");
return factoryBean;
}
index
<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.com">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="shortcut icon" href="#"/>
</head>
<body>
<h1>index</h1>
<div th:if="${session.account != null}">
<span th:text="${session.account.username}">欢迎回来</span><a href="/logout">退出</a>
</div>
<a href="/main">main</a>||<a href="/manage">manage</a>||<a href="admin">admin</a>
</body>
</html>