1、shiro简单介绍
简介
Apache Shiro 是一个强大且易用的Java安全框架,用于实现身份认证、鉴权、会话管理及加密功能。
框架提供了非常简单且易于上手的API,可以支持快速为web应用程序实现安全控制能力。
用法
- 鉴别用户身份,是否本系统注册的A用户
- 管理用户权限,是否有某个角色,或某些权限
- 即使没有web或EJB容器,也可以使用Session API
- 可以聚合一个或多个用户权限数据源并且以用户视图的形式统一表现出来
- 实现单点登录功能(SSO)
- 无需登录便可实现记住我这一功能
架构说明
看看下面的图:
图中涉及了若干个模块,关于每个模块的大致作用如下:
Subject
交互实体,对应于当前用户。
SecurityManager
安全管理器,Shiro最核心的模块,管理各安全模块的工作;
Authenticator
身份鉴别组件,执行和反馈用户的认证(登录),
该组件从Realm中获取用户信息。
Authentication Strategy
如果配置了多个Realm,该怎么协调?这就用到策略
Authorizer
权限认证,顾名思义,就是用于负责用户访问控制的模块。
SessionManager
会话管理器,在Web环境中Shiro一般会沿用Servlet容器的会话。
但脱离了Web环境就会使用独立的会话管理。
SessionDAO
执行会话持久化的工具
CacheManager
一个缓存管理器,可为 Shiro 的其他组件提供缓存能力。
Cryptography
加密组件,提供了大量简单易用的安全加密API
到这里,不需要为这么多的模块而苦恼,在使用Shiro时,只需要牢牢记住下面的实体关系,便不会产生理解上的困难。
简而言之
应用程序依赖于 Subject 实体来标识当前的用户,而SecurityManager 则通过Realm接口读取数据,进而实现 Subject 的关联管理。
实现
准备
pom文件中引入shiro依赖,生成如下实体
@Entity
@Table(name = "sys_permission")
@Proxy(lazy = false)
public class SysPermissionEntity implements Serializable{
@Id@GeneratedValue
private Integer id;//主键.
private String name;//名称.
private String resourceType;//资源类型,[menu|button]
private String url;//资源路径.
private String permission; //权限字符串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:view
private Long parentId; //父编号
private String parentIds; //父编号列表
private Integer available = 0;
@ManyToMany(fetch= FetchType.EAGER)
@JoinTable(name="SysRolePermission",joinColumns={@JoinColumn(name="permissionId")},inverseJoinColumns={@JoinColumn(name="roleId")})
private List roles;
@Entity
@Table(name = "sys_role")
@Proxy(lazy = false)
public class SysRoleEntity {
@Id@GeneratedValue
private Integer id;
private String role;
private String description;
private Integer status = 0;
@ManyToMany(fetch= FetchType.EAGER)
@JoinTable(name = "SysRolePermission",joinColumns = {@JoinColumn(name = "roleId")},inverseJoinColumns = {@JoinColumn(name = "permissionId")})
private List permissions;
@ManyToMany
@JoinTable(name = "SysUserRole",joinColumns = {@JoinColumn(name = "roleId")},inverseJoinColumns = {@JoinColumn(name = "id")})
private List users;
@Entity
@Table(name = "user_info")
@Proxy(lazy = false)
public class SysUserEntity implements Serializable {
@Id@GeneratedValue
private Integer id;
private String userName;
private String nickName;
private String password;
private String salt;
private Integer status = 0;
@ManyToMany(fetch= FetchType.EAGER)
@JoinTable(name = "SysUserRole",joinColumns = {@JoinColumn(name = "id")},inverseJoinColumns = {@JoinColumn(name = "roleId")})
private List roleList;// 一个用户具有多个角色
/**
* 密码盐.
* @return
*/
public String getCredentialsSalt(){
return this.userName+this.salt;
}
//重新对盐重新进行了定义,用户名+salt,这样就更加不容易被破解
Shiro 配置
首先要配置的是ShiroConfig类,Apache Shiro 核心通过 Filter 来实现,就好像SpringMvc 通过DispachServlet 来主控制一样。通过URL规则来进行过滤和权限校验,所以我们需要定义一系列关于URL的规则和访问权限。
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ShiroConfig {
/**
* 创建ShiroFilter对象,设置相关的属性
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//定义url拦截链
Map filterMap = new LinkedHashMap<>();
//配置不拦截的链接 顺序判断 一般/**放到最后
filterMap.put("/static/**","anon");
//配置退出过滤器
filterMap.put("/logout","logout");
//
filterMap.put("/**","authc");
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授权界面
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
/**
* 自定义Realm
*/
@Bean
public MyShiroRealm ShiroRealm(){
return new MyShiroRealm();
}
/**
* 创建安全管理器对象,关联自定义Realm
*/
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(ShiroRealm());
return securityManager;
}
/**
* 开启Shiro注解支持
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 处理异常(未配置无权限时不会返回URL,不能跳转到指定页面)
* @return
*/
@Bean(name="simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver
createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理
mappings.setProperty("UnauthorizedException","403");
r.setExceptionMappings(mappings); // None by default
r.setDefaultErrorView("error"); // No default
r.setExceptionAttribute("ex"); // Default is "exception"
//r.setWarnLogCategory("example.MvcLogger"); // No default
return r;
}
}
MyShiroRealm.java
@Component
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
UserInfoRepository userInfoRepository;
/**
* 授权(接口保护,验证接口调用权限时调用)
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals){
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
System.out.println("shiro授权");
SysUserEntity userEntity = (SysUserEntity)principals.getPrimaryPrincipal();
for(SysRoleEntity role:userEntity.getRoleList()){
authorizationInfo.addRole(role.getRole());
for(SysPermissionEntity p:role.getPermissions()){
authorizationInfo.addStringPermission(p.getPermission());
}
}
/**
* 也可以使用添加set集合authorizationInfo.setStringPermissions(permissions);
* permissions是从数据库查询到的用户对应的权限
*/
return authorizationInfo;
}
/**
* 认证(登录时调用)
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = (String) authenticationToken.getPrincipal();
//通过username从数据库中查找 User对象,如果找到,没找到.
//实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
SysUserEntity userInfo = userInfoRepository.findSysUserEntityByUserNameEquals(username);
if(userInfo == null){
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
userInfo, //用户名
userInfo.getPassword(), //密码
ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+salt
getName() //realm name
);
return authenticationInfo;
}
}
使用注解
@RestController
@RequestMapping("/userInfo")
public class UserController {
/**
* 用户查询.
* @return
*/
@RequestMapping("/userList")
@RequiresPermissions("userInfo:view")//权限管理;
public String userInfo(){
return "userInfo";
}
/**
* 用户添加;
* @return
*/
@RequestMapping("/userAdd")
@RequiresPermissions("userInfo:add")//权限管理;
public String userInfoAdd(){
return "userInfoAdd";
}
/**
* 用户删除;
* @return
*/
@RequestMapping("/userDel")
@RequiresPermissions("userInfo:del")//权限管理;
public String userDel(){
return "userInfoDel";
}
}