Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务。是一套流行的API框架,可以帮助开发人员快速构建API文档,还可以方便测试项目各项功能。
谈及两个问题:
Swagger如何集成?
maven引入依赖,配置swagger,设置静态资源映射
Swagger如何使用?
@ApiOperation, @ApiImplicitParams, @ApiImplicitParam
关于Swagger的使用已经发表博客,详情见:https://blog.csdn.net/Nerver_77/article/details/81476073
关于权限管理和shiro的入门之前也整理了博客,详情见:
权限管理:https://blog.csdn.net/Nerver_77/article/details/81318299
Shiro入门:https://blog.csdn.net/Nerver_77/article/details/81318357
下面要说到的是Guns框架中关于Shiro这一框架的集成和使用:
1. 首先,要明确的一个问题是权限管理内容:用户 – 角色 – 资源(权限) 三者之间关系
2. Shiro权限管理配置
2.1 依赖导入:
org.apache.shiro
shiro-core
slf4j-api
org.slf4j
org.apache.shiro
shiro-spring
org.apache.shiro
shiro-ehcache
slf4j-api
org.slf4j
org.ehcache
ehcache
2.2 shiro配置 ShiroConfig.java 这里记录下常用配置
/**
* 安全管理器:权限管理的核心配置
*/
@Bean
public DefaultWebSecurityManager securityManager(CookieRememberMeManager rememberMeManager, CacheManager cacheShiroManager, SessionManager sessionManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置管理器成员
securityManager.setRealm(this.shiroDbRealm());
securityManager.setCacheManager(cacheShiroManager);
securityManager.setRememberMeManager(rememberMeManager);
securityManager.setSessionManager(sessionManager);
return securityManager;
}
/**
* Shiro的过滤器链:shiro内置过滤器
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
/**
* 默认的登陆访问url
*/
shiroFilter.setLoginUrl("/login");
/**
* 登陆成功后跳转的url
*/
shiroFilter.setSuccessUrl("/");
/**
* 没有权限跳转的url
*/
shiroFilter.setUnauthorizedUrl("/global/error");
/**
* 配置shiro拦截器链
*
* anon 不需要认证
* authc 需要认证
* user 验证通过或RememberMe登录的都可以
*
* 当应用开启了rememberMe时,用户下次访问时可以是一个user,但不会是authc,因为authc是需要重新认证的
*/
Map hashMap = new LinkedHashMap<>();
//除一下声明无需认证的资源外,其他资源均需要认证authc。
//注意排序顺序,由于采用了LinkedHashMap,有序链表,自顶向下,优先级依次降低。
hashMap.put("/static/**", "anon");
hashMap.put("/login", "anon");
hashMap.put("/global/sessionError", "anon");
hashMap.put("/kaptcha", "anon");
hashMap.put("/**", "user");
shiroFilter.setFilterChainDefinitionMap(hashMap);
return shiroFilter;
}
2.3 自定义Realm ShiroDbRealm.java 重点
上述博文中也提到了Realm的作用,这里是实际应用Realm中,继承AuthorizingRealm类,需要实现认证和授权方法。
public class ShiroDbRealm extends AuthorizingRealm {
/**
* 登录认证:用户subject通过token进行登录,之后进入shiro登录人认证模块,进行token认证。
* 最后返回认证信息SimpleAuthenticationInfo。
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
throws AuthenticationException {
//自定义了Dao层的数据查询接口
IShiro shiroFactory = ShiroFactroy.me();
//认证token为Username和Password
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
/*
* 这里进入自定义的数据查询接口根据username查询对应user
* 如果user == null 会抛出异常,响应前端登录失败信息。
*/
User user = shiroFactory.user(token.getUsername());
//将返回的user设置成shiroUser
//这里的shiroUser:自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息。
ShiroUser shiroUser = shiroFactory.shiroUser(user);
//在生成认证信息的时候,会将user中的password进行加盐操作,之后进行MD5加密。和数据库中的password进行比对,比对成功
//即通过认证,这里的user需要说明,是从token中获取的。
SimpleAuthenticationInfo info = shiroFactory.info(shiroUser, user, super.getName());
return info;
}
/**
* 权限认证:通过认证的用户subject,进行权限的管理,shiroUser中携带了用户的更多信息。
* ShiroUser的成员变量:
* public Integer id; // 主键ID
* public String account; // 账号
* public String name; // 姓名
* public Integer deptId; // 部门id
* public List roleList; // 角色集
* public String deptName; // 部门名称
* public List roleNames; // 角色名称集
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//调用静态方法从spring容器中获取接口实例
IShiro shiroFactory = ShiroFactroy.me();
//获取认证通过的主体subject身份信息
ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
//获取shiroUser的角色集
List roleList = shiroUser.getRoleList();
//构建当前shirouser的权限集和角色名集
Set permissionSet = new HashSet<>();
Set roleNameSet = new HashSet<>();
//遍历角色集
for (Integer roleId : roleList) {
//根据角色ID查询对应的权限集
List permissions = shiroFactory.findPermissionsByRoleId(roleId);
if (permissions != null) {
//遍历对应角色ID的权限集
for (String permission : permissions) {
if (ToolUtil.isNotEmpty(permission)) {
//非空的加入到构建的权限集合中。
permissionSet.add(permission);
}
}
}
String roleName = shiroFactory.findRoleNameByRoleId(roleId);
//将角色ID对应的角色名称也加入构建角色名集合中。
roleNameSet.add(roleName);
}
//创建授权信息对象,将对应shiroUser的权限集和角色名集加入授权信息对象并返回。
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permissionSet);
info.addRoles(roleNameSet);
return info;
}
}
3. 自定义注解@Permission:关于自定义注解上文中也提到过@Bussinesslog的自定义注解,类比学习下。
Shiro框架中也含有权限管理的注解:@RequiresPermissions("/xxx/xxx")
自定义注解@Permission,利用AOP的方式进行权限管理:参数需要说明,是角色ID,不是请求URL。
其他注解使用和含义可以参考上文,这里就不赘述了。
/**
* 权限注解 用于检查权限 规定访问权限
*
* @example @Permission({roleID1,roleID2})
* @example @Permission
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Permission {
String[] value() default {};
}
4. PermissionAop:采用@Permission注解结合AOP进行权限管理。
//在ShiroConfig中我们下应该先进行shiro授权注解方式进行拦截,AOP式方法级别的权限检查。
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
构建AOP:PermissionAop.java
@Aspect
@Component
public class PermissionAop {
//声明切点,对@Permission注解进行拦截。
@Pointcut(value = "@annotation(com.stylefeng.guns.common.annotion.Permission)")
private void cutPermission() {
}
//通知类型为环绕通知
@Around("cutPermission()")
public Object doPermission(ProceedingJoinPoint point) throws Throwable {
MethodSignature ms = (MethodSignature) point.getSignature();
Method method = ms.getMethod();
//通过注解接口获得实例
Permission permission = method.getAnnotation(Permission.class);
//获取注解中的唯一成员变量value集合
Object[] permissions = permission.value();
//判断value集合是否为空 -- 刚才指出了value中包含的是角色ID集
if (permissions == null || permissions.length == 0) {
//检查全体角色
boolean result = PermissionCheckManager.checkAll();
if (result) {
return point.proceed();
} else {
throw new NoPermissionException();
}
} else {
//检查指定角色
boolean result = PermissionCheckManager.check(permissions);
if (result) {
return point.proceed();
} else {
throw new NoPermissionException();
}
}
}
}
这里需要说明的是PermissionCheckManager中的检查方法:checkAll()和check()。
@Override
public boolean checkAll() {
//从http中获取请求request
HttpServletRequest request = HttpKit.getRequest();
//通过ShiroKit工具类获取shiroUser对象
ShiroUser user = ShiroKit.getUser();
//对象判空处理
if (null == user) {
return false;
}
//对request请求进行分割 因为资源(权限)都是URL形式进行存储的,当用户subject进行访问的时候,request请求应该进行处理后进行比对。
String requestURI = request.getRequestURI().replaceFirst(ConfigListener.getConf().get("contextPath"), "");
String[] str = requestURI.split("/");
if (str.length > 3) {
requestURI = "/" + str[1] + "/" + str[2];
}
//判断是否有requestURL权限
if (ShiroKit.hasPermission(requestURI)) {
return true;
}
return false;
}
@Override
public boolean check(Object[] permissions) {
ShiroUser user = ShiroKit.getUser();
if (null == user) {
return false;
}
//对指定角色ID进行权限检查,采用工具类对角色ID集进行处理
String join = CollectionKit.join(permissions, ",");
//验证当前shiroUser属于join角色ID集中的任意一个角色
if (ShiroKit.hasAnyRoles(join)) {
return true;
}
return false;
}
5. Shiro标签:在前端页面中进行页面展示控制的标签。
例:根据权限的不同,我们要根据不同角色进行有区分的内容展示。针对普通用户,只有对信息的查看权限,没有增加、修改、删除的权限。
因此,我们在前端页面渲染的时候要进行有选择的区分显示。
根据之前做过的订单管理的Demo,其中order.html中的按钮就进行了权限的显示控制,通过Shiro标签进行判断内容是否应被显示。
@if(shiro.hasPermission("/order/add")){
<#button name="添加" icon="fa-plus" clickFun="Order.openAddOrder()"/>
@}
@if(shiro.hasPermission("/order/update")){
<#button name="修改" icon="fa-plus" clickFun="Order.openOrderDetail()" space="true"/>
@}
@if(shiro.hasPermission("/order/delete")){
<#button name="删除" icon="fa-plus" clickFun="Order.delete()" space="true"/>
@}
6. 权限管理步骤:在Guns框架中如何使用权限管理。
Guns框架中的权限管理构成:用户管理、角色管理、菜单管理。
1. 通过Guns-admin的菜单管理进行资源权限的添加。
2. 在角色管理中,建立角色并进行权限配置。
3. 在用户管理中,建立用户并将其分配给指定的角色。
4. 在Controller层中需要进行权限控制的方法添加@Permission注解。
5. 前端页面如果需要对权限内容进行区分显示,采用Shiro的hasPermission标签进行配置。
8/7/2018 4:55:34 PM