这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战
使用权限
用户登录之后,就会读取用户的角色权限名并将其放入权限存储中。当调用被标注需要权限的方法时,就会从权限存储对象中读取是否有该权限,进而执行
数据库表: user表:id,account,password,name,role_id role表:id,name,enname
修改Realm类,用于身份验证和获取角色权限
public class Realm extends AuthorizingRealm {
@Resource
UserService userService;
@Override
/*权限配置*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollectionprincipalCollection) {
System.out.println("授权=====");
// 获取当前登录的用户
Subject subject = SecurityUtils.getSubject();
// 这个user即在密码认证中闯入的user,所以它携带着角色权限信息
User user = (User) subject.getPrincipal();
// 权限存储对象,用于存储查询出的用户的角色及权限(如果有的话)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRole(user.getRoleName());
return info;
}
@Override
/*身份验证*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationTokenauthenticationToken) throws AuthenticationException {
System.out.println("认证===");
// 获取输入的帐号密码
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
User user = userService.selectAccount(token.getUsername());
if(user==null){
return null;
}
// shiro后台验证密码是否正确
return new SimpleAuthenticationInfo(user,user.getPassword(),getName());
}
}
修改配置文件,增加扫描shiro配置类注解
开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
@DependsOn({ "lifecycleBeanPostProcessor" })
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = newDefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = newAuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager());
return authorizationAttributeSourceAdvisor;
}
在需要使用权限验证的地方增加权限要求@RequiresRoles 以controller层为例
@RequiresRoles("check")
@RequestMapping({"/getImages"})
public Map<String,Object> getImages(){
HashMap< String, Object > map = new HashMap<>();
// 获取待审核的图片列表
List<Image> imageList = imageService.selectByFlag(staticConstant.UNCHECK);
// 获取所有信息放入list集合
List< Map< String, Object > > maps = forEachImageList(imageList);
map.put("list", maps);
map.put("message","返回待审核图片路径集合");
return map;
}
退出登录 在路径拦截map中配置
map.put("/login/logout","logout");
无需在controller中自己写,访问该路径即可退出登录
如果需要使用swagger,需要在拦截路径中放行swagger资源
map.put("/doc.html","anon");
map.put("/swagger-ui.html", "anon");
map.put("/swagger-resources/**", "anon");
自定义Token验证
一般来说,shiro生成返回token后,浏览器会自动在cookie中添加token值用于身份验证。当vue无法手动插入cookie时,可以使用自定义的session验证来通过,无序从cookie中读取。
获取sessionId,原本是根据sessionKey来获取一个sessionId 重写的部分多了一个把获取到的token设置到request的部分。这是因为app调用登陆接口的时候,是没有token的,登陆成功后,产生了token,我们把它放到request中,返回结果给客户端的时候,把它从request中取出来,并且传递给客户端,客户端每次带着这个token过来,就相当于是浏览器的cookie的作用,也就能维护会话了
1.创建自定义sessionManager
public class shiroSession extends DefaultWebSessionManager {
private static final String TOKEN = "token";
private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
public shiroSession(){
super();
}
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
//获取请求头中的 AUTH_TOKEN 的值,如果请求头中有 AUTH_TOKEN 则其值为sessionId。shiro就是通过sessionId 来控制的
String sessionId = WebUtils.toHttp(request).getHeader(TOKEN);
if (StringUtils.isEmpty(sessionId)){
//如果没有携带id参数则按照父类的方式在cookie进行获取sessionId
return super.getSessionId(request, response);
} else {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return sessionId;
}
}
}
2.在shiro配置文件中应用自定义的sessionManager
@Bean
public SessionManager sessionManager(){
// 实现我们写的shiroSession
shiroSession shiroSession = new shiroSession();
shiroSession.setSessionDAO(new EnterpriseCacheSessionDAO());
return shiroSession;
}
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
DefaultWebSecurityManager defaultWebSecurityManager = newDefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(realm());
// 将自定义的session管理放入
defaultWebSecurityManager.setSessionManager(sessionManager());
return defaultWebSecurityManager;
}
3.测试 登录获取token
总结
shiro的具体执行流程:
1.通过controller请求时,会触发realm的认证方法AuthenticationInfo(通过token连接)
具体操作:realm的AuthenticationInfo用于验证数据是否正确和读取数据库数据
如果通过认证,此时数据库的user就会变成subject
2.realm的授权方法AuthorizationInfo会对当前用户Subject进行授权
具体操作:realmd的AuthorizationInfo会从读取的数据为当前用户授权(addStringPermission)
将数据库的user的权限赋给subject
3.DefaultWebSecurityManager会关联realm和ShiroFilterFactoryBean
具体操作:setRealm(realm) :manager获取realm的数据
4.ShiroFilterFactoryBean设置资源权限,设置登录和未授权跳转页面
具体操作:1.setSecurityManager(defaultWebSecurityManager); 通过manager获取realm的数据
2.map.put("/toadd","perms[user:add]"); 设置资源的权限,并将map放入过滤器
- setXXXurl("") 设置跳转页面
将资源设置和获取的数据进行对比,成功就访问,不成功跳转未授权页面
5.controller根据shiro返回对应的信息