shiro
1.shiro的授权控制
* 基于过滤器的授权控制
anon 开放权限,用户不需要登录就可以访问
authc 需要登录认证
perms[user] 需要某个或某些权限才能通过,perms[“user,admin”],当有多个参数时必须每个参数都通过
roles[admin] 某个或某些角色才能通过,roles[“admin,user”],当有多个参数时必须每个参数都通过
* 基于注解的授权控制
@RequiresPermissions(value = {"添加用户","修改用户"})
@RequiresRoles(value = {"系统管理员"})
2.注解授权控制注意:
过滤器的授权控制,用户权限不足时通过shiroFilterFactoryBean.setUnauthorizedUrl("/autherror")设置跳转页面;而基于注解授权控制,如果权限不足则会抛出异常500到浏览器,因此需要自定义异常处理类补获异常
3.shiro三大功能:
身份认证,用户授权,会话管理
4.使用shiro会话管理功能解决单点登录问题
在web应用中,使用subject.login(token)身份认证成功后,实际上将用户信息存到了HttpSession中,shiro提供了三个默认的会话管理:
* DefaultSessionManager:用于JavaSE环境
* ServletContainerSessionManager:用于Web环境,直接使用servlet容器的会话(HttpSession)。
* DefaultWebSessionManager:用于web环境,自定义会话管理;使用自定义会话管理,将用户信息保存在redis中,实现单点登录
5.自定义session管理器实现单点登录原理:
配置SessionDao和SessionManager,当用户第一次登录时,将用户信息信息存储在redis中,key是随机ID,value是用户身份信息和授权信息,并将随机ID作为令牌返回给用户;当用户再次访问服务器,请求头key:Authorization,value:随机ID,然后根据ID从redis中查询用户信息,完成单点登录
6.Postman清除cookie信息:
点击Send按钮下方的Cookies
shiro测试案例
1.定义登录Controller
@RestController
public class LoginController{
@GetMapping("/login")
public String doLogin(String username,String password){
//对密码加密,参数1:需要加密密码,参数2:对密码加盐(通常使用用户名作为盐),参数3:加密次数
try{
assword = new Md5Hash(password,username,2);
UsernamePasswordToken upToken = new UsernamePasswordToken(username,password);
Subject subject = SecurityUtils.getSubject();
subject.login(upToken);
return "登录成功";
}catch(AuthenticationException e){
return e.getMessage();
}
}
@RequiresRoles(value = {"系统管理员"})
@RequiresPermissions(value = {"user-save","user-delete"})
@GetMapping("/findAnnotation")
public String doFindAnnotation(){
return "注解配置,查询到了页面...";
}
@GetMapping("/findFilter")
public String doFindFilter(){
return "过滤器配置,查询到了页面...";
}
}
2.自定义安全数据源
@Component
public class CustomerRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
public void setName(String name) {
super.setName("customerRealm");
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//安全数据与认证的第一个参数对应
User user = (User) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> roleSet = new HashSet<>();
Set<String> permissionSet = new HashSet<>();
for (Role role : user.getRoles()) {
roleSet.add(role.getName());
for (Permission permission : role.getPermissions()) {
permissionSet.add(permission.getName());
}
}
info.setRoles(roleSet);
info.setStringPermissions(permissionSet);
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
String username = upToken.getUsername();
String password = new String(upToken.getPassword());
User user = userService.findByName(username);
if (user == null || !user.getPassword().equals(password))
throw new AuthenticationException("账号密码不匹配...");
//保存认证信息,参数1是安全参数,认证获得的就是第一个参数
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, this.getName());
return info;
}
}
3.配置shiro注解配置类
@Configuration
public class ShiroConfiguration {
//安全管理器
@Bean
public SecurityManager securityManager(CustomerRealm customerRealm) {
return new DefaultWebSecurityManager(customerRealm);
}
//过滤器
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/autherror?code=1"); //设置登录页面
shiroFilterFactoryBean.setUnauthorizedUrl("/autherror?code=2"); //授权失败跳转页面
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/user/hello", "anon"); //hello资源不需要认证
filterMap.put("/user/**", "authc"); //需要认证
//filterMap.put("/findFilter", "perms[user-find,user-update]"); //权限
//filterMap.put("/findFilter", "roles[系统管理员]"); //角色
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
//配置shiro注解支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
4.自定义异常处理类
@ControllerAdvice
public class BaseExceptionHandler {
@ExceptionHandler(value = AuthorizationException.class)
@ResponseBody
public String error(HttpServletRequest request, HttpServletResponse response,AuthorizationException e) {
return "权限不足...";
}
}
shiro单点登录
1.自定义shiro会话管理器
public class CustomerSessionManager extends DefaultWebSessionManager {
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
//从请求头中获取Authorization对应的值
String id = WebUtils.toHttp(request).getHeader("Authorization");
if (StringUtils.isEmpty(id)) {
//如果ID为空,说明用户当前在登录认证,生成一个ID,并存入Authorization中
return super.getSessionId(request, response);
}
//设置sessionId的来源,header
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
//设置sessionId的值,id
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
//设置sessionId有效
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return id;
}
}
2.配置shiro注解配置类
@Configuration
public class ShiroConfiguration {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
//安全管理器
@Bean
public SecurityManager securityManager(CustomerRealm customerRealm
, SessionManager sessionManager, RedisCacheManager redisCacheManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(customerRealm);
securityManager.setSessionManager(sessionManager);
securityManager.setCacheManager(redisCacheManager);
return securityManager;
}
//过滤器
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/autherror?code=1"); //设置登录页面
shiroFilterFactoryBean.setUnauthorizedUrl("/autherror?code=2"); //授权失败跳转页面
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/user/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
//配置shiro注解支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
//redis管理器
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
return redisManager;
}
//redis缓存管理器
@Bean
public RedisCacheManager redisCacheManager(RedisManager redisManager) {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager);
return redisCacheManager;
}
//redis存储工具
@Bean
public RedisSessionDAO redisSessionDao(RedisManager redisManager) {
RedisSessionDAO redisSessionDao = new RedisSessionDAO();
redisSessionDao.setRedisManager(redisManager);
return redisSessionDao;
}
//自定义会话管理器
@Bean
public DefaultWebSessionManager sessionManager(RedisSessionDAO redisSessionDao) {
CustomerSessionManager customerSessionManager = new CustomerSessionManager();
customerSessionManager.setSessionDAO(redisSessionDao);
return customerSessionManager;
}
}
shiro整合spring boot坐标
<!--shiro和spring整合-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--shiro核心包-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<!--shiro与redis整合-->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.0.0</version>
</dependency>