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
|
@Bean public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); return defaultAdvisorAutoProxyCreator; }
@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 已经可以说了解怎么用了,其实还有很多问题:
- 首先是错误页显示,没有权限理论应该跳转到我们配置的无权限的页面,但是并没有
- 我们不断的访问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
|
@Bean public SimpleMappingExceptionResolver simpleMappingExceptionResolver() { SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver(); Properties properties = new Properties(); 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
|
@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)的功能,比如访问一些网站时,关闭了浏览器下次再打开时还是能记住你是谁,下次访问时无需再登录即可访问,基本流程如下:
- 首先在登录页面选中RememberMe然后登录成功;如果是浏览器登录,一般会把RememberMe的Cookie写到客户端并保存下来;
- 关闭浏览器再重新打开;会发现浏览器还是记住你的;
- 访问一般的网页服务器端还是知道你是谁,且能正常访问;
ShiroConfig相关配置
记住我cookie
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
@Bean public SimpleCookie rememberMeCookie() { SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
simpleCookie.setHttpOnly(true); simpleCookie.setPath("/"); simpleCookie.setMaxAge(2592000); return simpleCookie; }
|
记住我管理器
1 2 3 4 5 6 7 8 9 10 11 12 13
|
@Bean public CookieRememberMeManager rememberMeManager() { CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); cookieRememberMeManager.setCipherKey(Base64.decode("6ZmI6I2j5Y+R5aSn5ZOlAA==")); return cookieRememberMeManager; }
|
记住我Filter
1 2 3 4 5 6 7 8 9 10 11 12
|
@Bean public FormAuthenticationFilter formAuthenticationFilter() { FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter(); formAuthenticationFilter.setRememberMeParam("rememberMe"); return formAuthenticationFilter; }
|
修改相关代码
修改安全事务管理器
在SecurityManager中打开记住我,之前是注释掉的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
@Bean(name = "securityManager") public SecurityManager securityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 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
|
@Bean(name = "shirFilter") public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) { ...
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) {
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password, rememberMe); ... }
|
更改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/> 密 码:<input type="password" name="password"/><br/>
|
测试
上面的过程已经配置完成,现在进行测试,重启项目,使用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对长度有一定的限制。