搭建shiro环境
1:导入boot项目中要用到的shiro依赖
<!--shiro部分-->
<!--shiro核心源码-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!--开启后端shiro标签支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--html中使用shiro标签-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
2:编写shiro的配置类
作用:开启后端shiro注解(重要)
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
= new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
作用:启用shiro thymeleaf标签支持(重要)
/**
* 启用shiro thymeleaf标签支持
* @return
*/
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
作用:配置shiro的拦截器工厂bean(这里比较容易踩坑)
属性 | 含义 |
user | 登录且访问对应的url有相应的权限角色才能访问 |
authc | 必须认证了才能访问(进行鉴权) |
anon | 无需任何权限或者角色就可以访问 |
perms[] | 指定的权限都要有才能访问 |
roles[] | 指定的角色都要有才能访问 |
setUnauthorizedUrl | 登录了但是没有相应权限时的跳转页面 |
setLoginUrl | 设置没有登录时的默认跳转页面 |
setFilterChainDefinitionMap | 设置资源访问权限(过滤规则) |
setFilters | 设置我们的自定义拦截器(应用于url) |
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//这里一定要用LinkedHashMap避免设置的过滤规则乱序
LinkedHashMap<String, String> chain = new LinkedHashMap<String, String>();
//设置自定义拦截器
LinkedHashMap<String, Filter> filters = new LinkedHashMap<>();
filters.put("jwtFilter", new jwtFilter());
//这些路径下的资源不需要权限
chain.put("/login", "anon"); // 登录链接不拦截
chain.put("/css/**", "anon");
chain.put("/img/**", "anon");
chain.put("/js/**", "anon");
chain.put("/lib/**", "anon");
chain.put("/needRole", "roles[zzh]");
chain.put("/noNeedRole", "anon");
chain.put("/unAuth", "anon");
chain.put("/testCustomExpection", "anon");
chain.put("/needPerms", "perms[zzhperms,zzhperms2]");
chain.put("/noNeedPerms", "anon");
//测试jwtFilter
chain.put("/shiroCheck", "jwtFilter");
//chain.put("/api/auth/**", "noSessionCreation,jwtFilter");
//chain.put("/api/auth/**", "noSessionCreation");
//其他路径需要权限,这个一定要写在最后,这样所有的过滤规则才会生效
chain.put("/**", "authc");
//设置资源访问权限
bean.setFilterChainDefinitionMap(chain);
bean.setSecurityManager(securityManager());
//设置没有登录时的默认跳转页面
bean.setLoginUrl("/login");
//登录了但是没有相应权限时的跳转页面
bean.setUnauthorizedUrl("/unAuth");
bean.setFilters(filters);
return bean;
}
遇到的坑(设置的一些过滤规则不生效)
由于过滤链是从上往下顺序执行的,chain要用LinkedHashMap不要用hashMap,hashMap的存储是无序的可能chain.put("/**", “authc”);先遍历到了,其他的访问路径也就不需要访问拦截了
还没解决问题?
chain.put("/**", “authc”);这个记得一定要写在最后,这样所有的过滤规则才会生效
这些配置比较通用读者可以自行配置(比较机械的代码)
不做过多说明
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
// 注入安全管理器
advisor.setSecurityManager(securityManager());
return advisor;
}
/**
* @param
* @method 配置安全管理器
*/
@Bean
public SecurityManager securityManager() {
DefaultSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setCacheManager(shiroCacheManager);
securityManager.setRealm(loginRealm());
securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* @param
* @method 注入realm
*/
@Bean
public loginRealm loginRealm() {
return new loginRealm();
}
/**
* @param
* @method DefaultWebSessionManager的配置
*/
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
SimpleCookie cookie = new SimpleCookie("myZzhSessionId");
sessionManager.setSessionIdCookie(cookie);
sessionManager.setSessionDAO(sessionDAO());
return sessionManager;
}
/**
* @param
* @method 这里配置shiro的会话底层的缓存管理器,底层用的是redis
*/
@Bean
public SessionDAO sessionDAO() {
shiroSessionDao shiroSessionDao = new shiroSessionDao(shiroCacheManager);
return shiroSessionDao;
}
编写测试controller
标签 | 属性 | 解释 |
@RequiresRoles | value = {“shiro”, “zzh”}, logical = Logical.OR | 权限有一个就能访问此接口 |
@RequiresPermissions | value = {“add”, “select”}, logical = Logical.AND | 权限都有才能访问此接口 |
@Controller
@RequestMapping("/testShiroAno")
public class testShiroAnoController {
@RequiresPermissions("shiroAdd")
@RequestMapping("/add")
@RequiresRoles({"shiro"})
public String add() {
return "add";
}
@RequestMapping("/delete")
@RequiresPermissions("shiroDelete")
public String delete() {
return "delete";
}
@RequestMapping("/alter")
public String alter() {
return "alter";
}
@RequiresRoles(value = {"shiro", "zzh"}, logical = Logical.OR)
@RequestMapping("/select")
@RequiresPermissions("shiroSelect")
public String select() {
return "select";
}
}
编写测试loginRelam
当执行subject.login(token)的时候就会走我们Relam中的逻辑。这里我用的模拟数据,里面的逻辑如下。
用户 | 拥有的权限 | 拥有的角色 |
zzh | zzhperms和zzhperms2 | zzh |
shiro | shiroAdd和shiroDelete和shiroAlter | shiro |
如果参数二是loginUser那么shiro会自动把loginUser中的password与参数二中的password作比较(我们无需编写比较密码的逻辑)
SimpleAuthenticationInfo | 参数一 | 参数二 | 参数三 |
简单认证对象信息 | 认证的对象 | 数据库中的密码 | 就是realm的名称 |
simpleAuthorizationInfo |
简单授权对象信息,我们可以对其赋予权限 |
public class loginRealm extends AuthorizingRealm {
private Logger log = LoggerFactory.getLogger(loginRealm.class);
/**
* @param
* @return
* @function 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
log.info("授权。。。。。");
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
ArrayList<String> perms = new ArrayList<>();
Object key = principals.getPrimaryPrincipal();
loginUser loginUser = new loginUser();
try {
BeanUtils.copyProperties(loginUser, key);
} catch (Exception e) {
}
//TODO这里是模拟数据库中的权限表的情况
if (loginUser.getName().equals("shiro")) {
perms.add("shiroAdd");
perms.add("shiroDelete");
perms.add("shiroAlter");
simpleAuthorizationInfo.addRole("shiro");
simpleAuthorizationInfo.addStringPermissions(perms);
}
if (loginUser.getName().equals("zzh")) {
simpleAuthorizationInfo.addRole("zzh");
perms.add("zzhperms");
perms.add("zzhperms2");
simpleAuthorizationInfo.addStringPermissions(perms);
}
log.info("授权完成。。。。。");
return simpleAuthorizationInfo;
}
/**
* @param
* @return
* @function 认证 doGetAuthenticationInfo
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.info("认证....");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 1.获取用户输入的用户名
String username = token.getUsername();
// 2.获取用户输入的密码
String password = new String(token.getPassword());
loginUser loginUser = new loginUser();
loginUser.setName(username);
loginUser.setPassword(password);
SimpleAuthenticationInfo info =
new SimpleAuthenticationInfo(loginUser, "666", getName());
log.info("认证完成....");
return info;
}
}
遇到的坑(获取PrimaryPrincipal信息的时候无法进行相应类型的对象转换)
这里我们直接暴力点用BeanUtils.copyProperties(loginUser, key);直接进行俩个对象间的属性赋值即可
权限,标签,注解对照表
测试的时候大家可以对着我这张参照表来对应着来看哦,铁质门(我现在就用过这么些shiro的标签,以后如果遇到过新的标签注解,再来补上来吧)
前端控制标签 | 作用 |
shiro:hasPermission | 有此权限时显示按钮 |
shiro:hasAllPermissions | 有全部权限时显示 按钮 |
shiro:hasAnyPermissions | 有以下的任何一个权限时显示按钮 |
shiro:principal property=“name” | 获取已经登录用户的name属性的值 |
data-shiro-principal property=“password” | 获取已经登录用户的password属性的值 |
shiro:lacksPermission | 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过 |
shiro:lacksRole | 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。 |
shiro:user | 认证通过登录过的用户 |
shiro:guest | 没登录过的用户(访客) |
后端控制注解 | 可选参数 | 解释 |
@RequiresRoles(value = {“shiro”, “zzh”}, logical = Logical.OR) | value(权限)logical(规则) | 只有当前用户同时拥有shiro和zzh这俩个角色时才能访问这个接口 |
@RequiresPermissions(value = {“shiro”, “zzh”}, logical = Logical.OR) | value(权限)logical(规则) | 只有当前用户同时拥有shiro和zzh这俩个权限时才能访问这个接口 |
开始准备测试
直接编写一个add.html(由于是boot项目相应的路由规则不再啰嗦)注意导入th shiro标签支持
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
用shiro用户登录的效果图
用zzh用户登录的效果图(我这里是用了自定义异常,自定义异常传送门)