Shiro是一个功能强大、灵活的开源安全框架,可以干净地处理身份验证、授权、会话管理和加密。本文将记录Shiro相关的开发笔记。
Shiro官网给出的架构图:
从下图中可以看出Shiro主要有Subject(用户),ShiroSecurityManager(操作管理用户) 和Realm(操作安全数据进行授权,认证)组成
Shiro官网:Apache Shiro | Simple. Java. Security.
首先引入相关依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.8.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.8.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.8.0</version>
<scope>compile</scope>
</dependency>
自定义Realm
根据上面的框架,我们首先要编写一个自定义的realm用于处理数据如用户名,密码,访问权限等
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//new一个AuthorizationInfo()用于管理角色和权限
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//从Principal()中获取我们在下面 SimpleAuthenticationInfo 存入的user对象
User currentUser = (User) SecurityUtils.getSubject().getPrincipal();
//从user对象中读取我们perm值,perm在数据库中定义了,为权限信息
//Perm如user:vip
info.addStringPermission(currentUser.getPerm());
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//读取传入的Token
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//通过token中的信息取到user对象
User user = userService.getUserByName(token.getUsername());
//如果用户不存在
if(user==null){
//返回null, shiro抛出UnknownAccountException异常
return null;
}
//返回AuthenticationInfo
//包含三个参数Object principal, Object credentials, String realmName
//principal传入我们需要储存的对象,credentials即密码
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
其中 SimpleAuthorizationInfo 用于管理角色,权限等信息
SimpleAuthenticationInfo用于管理认证信息
编写shiro配置文件
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(defaultWebSecurityManager);
//创建一个map存储授权信息
Map<String, String> filterChainDefinitionMap=new LinkedHashMap<>();
//设置vip界面的访问权限为user:vip
filterChainDefinitionMap.put("/pages/vip","perms[user:vip]");
//设置Pages下的所有界面需认证才能访问
filterChainDefinitionMap.put("/pages/*","authc");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//设置登陆界面
bean.setLoginUrl("/toLogin");
//设置未授权错误界面
bean.setUnauthorizedUrl("noauth");
return bean;
}
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
//创建securityManager 对象,传入Realm
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean
public UserRealm userRealm(){
return new UserRealm();
};
}
编写登录控制器
@GetMapping("/login")
public String login(String username, String password, Model model){
//将前端传入的用户名和密码存入UsernamePasswordToken中
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//获取当前用户的subject 对象
Subject subject = SecurityUtils.getSubject();
try {
//登录
subject.login(token);
return "redirect:/index";
//UnknownAccountException 用户名错误
}catch (UnknownAccountException e){
model.addAttribute("error","Wrong username");
return "login";
//IncorrectCredentialsException 密码错误
}catch (IncorrectCredentialsException e){
model.addAttribute("error","wrong password");
return "login";
}
}