前后端分离项目中 springboot 集成 shiro 实现权限控制
原创
©著作权归作者所有:来自51CTO博客作者Jaemon的原创作品,请联系作者获取转载授权,否则将追究法律责任
文章目录
- 使用注解控制鉴权授权
- 使用 url配置控制鉴权授权
- 表结构
- jar 包依赖
- 代码说明
- 跨域问题解决
- 登录验证不进行重定向改为设置http状态
- 项目源码
- Reference
使用注解控制鉴权授权
注解
| 功能
|
@RequiresGuest
| 只有游客可以访问
|
@RequiresAuthentication
| 需要登录才能访问
|
@RequiresUser
| 已登录的用户或“记住我”的用户能访问
|
@RequiresRoles
| 已登录的用户需具有指定的角色才能访问
|
@RequiresPermissions
| 已登录的用户需具有指定的权限才能访问
|
使用 url配置控制鉴权授权
配置缩写
| 对应的过滤器
| 功能
|
anon
| AnonymousFilter
| 指定url可以匿名访问
|
authc
| FormAuthenticationFilter
| 指定url需要form表单登录,默认会从请求中获取username 、password ,rememberMe 等参数并尝试登录,如果登录不了就会跳转到loginUrl配置的路径。我们也可以用这个过滤器做默认的登录逻辑,但是一般都是我们自己在控制器写登录逻辑的,自己写的话出错返回的信息都可以定制嘛。 |
authcBasic
| BasicHttpAuthenticationFilter
| 指定url需要basic登录
|
logout
| LogoutFilter
| 登出过滤器,配置指定url就可以实现退出功能,非常方便
|
noSessionCreation
| NoSessionCreationFilter
| 禁止创建会话
|
perms
| PermissionsAuthorizationFilter
| 需要指定权限才能访问
|
port
| PortFilter
| 需要指定端口才能访问
|
rest
| HttpMethodPermissionFilter
| 将http请求方法转化成相应的动词来构造一个权限字符串,这个感觉意义不大,有兴趣自己看源码的注释
|
roles
| RolesAuthorizationFilter
| 需要指定角色才能访问
|
ssl
| SslFilter
| 需要https请求才能访问
|
user
| UserFilter
| 需要已登录或“记住我”的用户才能访问
|
表结构
表
| 注释
|
sys_user
| 用户表
|
sys_role
| 角色表
|
sys_user_role
| 用户角色关联表
|
sys_role_permission
| 角色权限关联表
|
sys_permission
| 权限表
|
jar 包依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
代码说明
身份认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 根据用户名获取用户密码
User user = userService.findUserByLoginName(token.getUsername());
if (user == null) {
throw new AuthenticationException(String.valueOf(ResponseEnum.ERROR_INCORRECT_UNAME_OR_PWD.code()));
}
ByteSource salt = ByteSource.Util.bytes(user.getSalt());
String password = user.getPassword();
String saltPassword = ShiroUtil.saltEncrypt(token.getPassword(), user.getSalt());
if (null == password) {
throw new AuthenticationException(String.valueOf(ResponseEnum.INVALID_USER.code()));
} else if (!password.equals(saltPassword)) {
throw new AuthenticationException(String.valueOf(ResponseEnum.PASSWORD_ERROR.code()));
}
return new SimpleAuthenticationInfo(token.getPrincipal(), token.getPassword(), salt, getName());
}
在执行 subject.login(token)
时 会调用 doGetAuthenticationInfo
方法代码
权限认证
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = (String) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
// 根据 用户名称 获取用户的 角色信息
Set<String> roles = userService.findUserRolesByLoginName(username);
// 设置该用户拥有的角色 @RequiresRoles
if (roles.size() > 0) {
simpleAuthorizationInfo.setRoles(roles);
}
// 设置用户的权限列表 @RequiresPermissions
Set<String> userPermissions = userService.findUserPermissionsByLoginName(username);
if (userPermissions.size() > 0) {
simpleAuthorizationInfo.setStringPermissions(userPermissions);
}
return simpleAuthorizationInfo;
}
在接口标明注解 @RequiresPermissions
或 @RequiresRoles
时会调用 doGetAuthorizationInfo
方法
// `/admin/**` 下的接口需要管理员权限, 等同于在对应接口上加 @RequiresRoles(value = {"admin", "user"}, logical = Logical.OR)
filterChainDefinitionMap.put("/admin/**", "roles[admin,user]");
// `/user/**` 下的接口需要的权限, 等同于在对应接口上加 @RequiresPermissions("order:query")
filterChainDefinitionMap.put("/user/**", "perms[user:query]");
或者 ShiroConfig
中 ShiroFilterFactoryBean
对象中配置了以上权限代码时也会调用 doGetAuthorizationInfo
方法
跨域问题解决
@Configuration
public class CorsConfig {
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
// 指定运行地址
corsConfiguration.setAllowedOrigins(Lists.newArrayList("http://192.168.10.1:8080"));
// 允许任何域名
// corsConfiguration.addAllowedOrigin(CorsConfiguration.ALL);
// 允许任何头部信息
corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);
// 允许所有请求类型
corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);
corsConfiguration.addExposedHeader("Authorization");
source.registerCorsConfiguration("/**", corsConfiguration);
FilterRegistrationBean bean = new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}
登录验证不进行重定向改为设置http状态
public class AiAuthenticationFilter extends FormAuthenticationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
throws Exception {
WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
}
// 修改 ShiroConfig 的 shiroFilter 方法
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
Map<String, Filter> filterMap = shiroFilterFactoryBean.getFilters();
filterMap.put("authc", new AiAuthenticationFilter());
shiroFilterFactoryBean.setFilters(filterMap);
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/unauth");
// 设置重定向接口地址
// shiroFilterFactoryBean.setLoginUrl("/api/user/v1/notLogin");
shiroFilterFactoryBean.setUnauthorizedUrl("/api/user/v1/notPerms");
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/api/external/**", "anon");
filterChainDefinitionMap.put("/api/user/v1/login", "anon");
filterChainDefinitionMap.put("/api/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
}
项目源码
项目源码 Github 地址
: springboot-shiro
Reference
- Shiro用starter方式优雅整合到SpringBoot中
- 教你 Shiro 整合 SpringBoot,避开各种坑