抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

SpringBoot整合Shiro(二)

权限功能校验

经过上面的过程,已经可以对用户的身份进行校验,但是这个时候,但是权限控制好像没有什么作用,因为我们使用admin用户登录之后,在浏览器上访问地址 /userInfo/del发现也是可以使用的,其实我们还少了以下步骤,也就是开启注解支持

开启Shiro的注解

在ShiroConfig中配置以下bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions)
*
* @return
*/
@Bean
public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}

/**
* 开启shiro 注解模式
* 可以在controller中的方法前加上注解
* 如 @RequiresPermissions("userInfo:add")
*
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}

在controller增加权限

1
2
3
4
5
6
7
8
@RequiresPermissions("userInfo:del")
@RequestMapping(value = "/del",method = RequestMethod.GET)
@ResponseBody
public String del(Model model) {
userService.del("baiyp");
return "删除用户名为baiyp用户成功";

}

添加@RequiresPermissions("userInfo:del")然后重启项目,再次使用amdin登录之后,在浏览器上调用http://localhost:9090/userInfo/del就会跳转到以下错误页。证明权限校验成功。

后台会报以下异常:调用未授权的方法

到此,shiro入门完了,我相信很多人对shiro 已经可以说了解怎么用了,其实还有很多问题:

  1. 首先是错误页显示,没有权限理论应该跳转到我们配置的无权限的页面,但是并没有
  2. 我们不断的访问http://localhost:8080/userInfo/view 发现每次都会去数据库查询权限,但是实际中我们的权限信息是不怎么会改变的,所以我们希望是第一次访问,然后进行缓存处理等等,这些会在后面的文章中。

解决 Whitelabel Error Page

页面显示为默认springboot展示的页面Whitelabel Error Page,具体页面如下,并没有跳转到我们之前配置的页面 。

配置异常处理器

新增SimpleMappingExceptionResolver

​ 也就是说shiroFilterFactoryBean.setUnauthorizedUrl(“/unauthorized”);无效!但是有时候我们需要向客户展示友好界面,所以,还需要如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 解决: 无权限页面不跳转 shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized") 无效
* shiro的源代码ShiroFilterFactoryBean.Java定义的filter必须满足filter instanceof AuthorizationFilter,
* 只有perms,roles,ssl,rest,port才是属于AuthorizationFilter,而anon,authcBasic,auchc,user是AuthenticationFilter,
* 所以unauthorizedUrl设置后页面不跳转 Shiro注解模式下,登录失败与没有权限都是通过抛出异常。
* 并且默认并没有去处理或者捕获这些异常。在SpringMVC下需要配置捕获相应异常来通知用户信息
*
* @return
*/
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
//这里的 /unauthorized 是页面,不是访问的路径
properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/unauthorized");
properties.setProperty("org.apache.shiro.authz.UnauthenticatedException", "/unauthorized");
simpleMappingExceptionResolver.setExceptionMappings(properties);
return simpleMappingExceptionResolver;
}

测试

然后重新启动程序,发现访问无权限的后台服务,就会跳转到我们配置的页面。

但是这个时候还有一个问题:访问不存在的后台服务,如http://localhost:8080/userInfo/xxxxx 这样后面随便乱打的,这种情况,依然还是返回了Whitelabel Error Page页面。

我们不想看到这种效果,所以还需要在ShiroConfig配置类中,添加以下bean

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 解决spring-boot2.0 Whitelabel Error Page
*
* @return
*/
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer() {
return factory -> {
ErrorPage error401Page = new ErrorPage(HttpStatus.UNAUTHORIZED, "/unauthorized");
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/404");
ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500");
factory.addErrorPages(error401Page, error404Page, error500Page);
};

再次访问http://localhost:8080/userInfo/xxxxx 因为服务根本不存在,就会跳转到404页面。

如果访问http://localhost:8080/userInfo/del 就会跳转到unauthorized.html 因为没有权限。

配置记住我

记住我功能在各各网站是比较常见的,实现起来也都差不多,主要就是利用cookie来实现,而shiro对记住我功能的实现也是比较简单的,只需要几步即可。

基本流程

​ Shiro提供了记住我(RememberMe)的功能,比如访问一些网站时,关闭了浏览器下次再打开时还是能记住你是谁,下次访问时无需再登录即可访问,基本流程如下:

  1. 首先在登录页面选中RememberMe然后登录成功;如果是浏览器登录,一般会把RememberMe的Cookie写到客户端并保存下来;
  2. 关闭浏览器再重新打开;会发现浏览器还是记住你的;
  3. 访问一般的网页服务器端还是知道你是谁,且能正常访问;

ShiroConfig相关配置

记住我cookie
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* cookie对象;会话Cookie模板 ,默认为: JSESSIONID 问题: 与SERVLET容器名冲突,重新定义为sid或rememberMe,自定义
*
* @return
*/
@Bean
public SimpleCookie rememberMeCookie() {
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//setcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:

//setcookie()的第七个参数
//设为true后,只能通过http访问,javascript无法访问
//防止xss读取cookie
simpleCookie.setHttpOnly(true);
simpleCookie.setPath("/");
//<!-- 记住我cookie生效时间30天 ,单位秒;-->
simpleCookie.setMaxAge(2592000);
return simpleCookie;
}
记住我管理器
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* cookie管理对象;记住我功能,rememberMe管理器
*
* @return
*/
@Bean
public CookieRememberMeManager rememberMeManager() {
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("6ZmI6I2j5Y+R5aSn5ZOlAA=="));
return cookieRememberMeManager;
}
记住我Filter
1
2
3
4
5
6
7
8
9
10
11
12
/**
* FormAuthenticationFilter 过滤器 过滤记住我
*
* @return
*/
@Bean
public FormAuthenticationFilter formAuthenticationFilter() {
FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
//对应前端的checkbox的name = rememberMe
formAuthenticationFilter.setRememberMeParam("rememberMe");
return formAuthenticationFilter;
}

修改相关代码

修改安全事务管理器

在SecurityManager中打开记住我,之前是注释掉的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 配置核心安全事务管理器
*
* @param shiroRealm
* @return
*/
@Bean(name = "securityManager")
public SecurityManager securityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置自定义realm.
securityManager.setRealm(shiroRealm);
//配置记住我
securityManager.setRememberMeManager(rememberMeManager());
...

return securityManager;
}
修改拦截规则

修改shirFilter中拦截请求的规则,将/**从authc 改为user

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* ShiroFilterFactoryBean 处理拦截资源文件问题。
* 注意:初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
* Web应用中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截
*
* @param securityManager
* @return
*/
@Bean(name = "shirFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
...

//其他资源都需要认证 authc 表示需要认证才能进行访问
// filterChainDefinitionMap.put("/**", "authc");
//其他资源都需要认证 authc 表示需要认证才能进行访问 user表示配置记住我或认证通过可以访问的地址
filterChainDefinitionMap.put("/**", "user");

...

return shiroFilterFactoryBean;
}
更改LoginController

接受前台记住我参数,然后传给usernamePasswordToken,将之前的那行注释掉。

1
2
3
4
5
6
7
8
9
10
11
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String loginUser(HttpServletRequest request, String username, String password, boolean rememberMe, Model model, HttpSession session) {

//对密码进行加密
//password=new SimpleHash("md5", password, ByteSource.Util.bytes(username.toLowerCase() + "shiro"),2).toHex();
//如果有点击 记住我
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password, rememberMe);
//注释掉
// UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password);
...
}
更改User实体类

User实体类必须实现序列化接口,否则报序列化异常。因为RememberMe会将用户信息加密然后以Cookie保存。

1
2
3
public class User implements Serializable {
...
}
更改Login.html

前台页面添加记住我复选框

1
2
3
4
5
6
7
8
9
10
...
<h1>欢迎登录</h1>
<h1 th:if="${msg != null }" th:text="${msg}" style="color: red"></h1>
<form action="/login" method="post">
用户名:<input type="text" name="username"/><br/>
&nbsp;&nbsp;码:<input type="password" name="password"/><br/>
<!--
<input type="checkbox" name="rememberMe" />记住我<br/>
<input type="submit" value="提交"/>
...

测试

上面的过程已经配置完成,现在进行测试,重启项目,使用admin登录,然后访问:http://localhost:8080/userInfo/view会跳转到登录页面,登录之后退出浏览器,再次点开访问刚才的页面,可以访问。

浏览器cookie查看

rememberMe 大概内容如下
1
blDl1IGoHN12pWm/dS8+PCwp81NwooEbdvkxMreWffEGcWtVeOUBusIPgjnUbByPhK9dxXuybhvUwjf5AVrYoACHHN3qim0A0Gw1jIPMYibzbcra+dYkBAQo3PKq+bPzDg99zSM60PcT4k7ZauMoGnZmTKrmgItZ2FytCFam9xUBgueIgSSocoHpG2yedJI9RqgCr9KNJVeSg9NKiSJ3cfqTjbjvHZ2yXzTym0735Pq9hZvJ0PicqqXtZphVzIXL4TfER7MRInKgD1GmLXdhGySKHrnvzpGrqUj4f4a1C6IjbTwRcE+Gf2nK9ydqcrHqAjlrbADn8uVInP9uqTDtHU0tiB73M6GQtwdXrMWXD9r75FhUCYZVA9ZxrMgxpISaVBygCWoLjQpR8SR/PRG40qP6lNqs/xjEVd+EUcfIIKdWTVQhCv4/HDCFxF2w5CFPY2KZqWmClLIPKjUVf+40Otur8/eLJZE9LSsb2GiKpZOnFy4/5rgVWLvPGWBcca/UaO/ZYJ/cANHP70/rmIsRblu42ZAXGZiIyI++S+hxDPgfTU44X2xKCKaFhQLkWV0akoXrmrwQV3aMcJrMusIL2lIk+Cyw0yhCxePPQQyuvHTRcm8LpFtVLm1nOqv+l2TRgIVjhKXutIRIFWNPP05q0+LSnJ+3xYSO9Kvo36PN+EqjU2vTy5oTbNaqkm070NXclKju0f37+c9J4VVz96I3dvSHqBgYevRDUGOdw5LuDWP7lh83l8ptfn1m5Mn2Hr9TmLrbscAN8c00sorGOaB1mcjlQyH7uYoAF/i03RfCD7uA1RMd+6NSXyeSFehmueVn58rv3BHPVS9UlZxoiGXoPkP3sQc0QKdccCfN0Iolt2ZnPmvZDpx3VA963zzqfMYYYc6T/3q0XCyga6lVdMqsi+llkTsydQWx3rtRSLfyTkHTpDxmZv5qfR+f8mDPRJ6qI0pKf8SuTr0=

其实是将用户信息加密后放到前台的(包含密码),在项目中user对象信息过于庞大,不能全部存入Cookie,Cookie对长度有一定的限制。

评论