工程代码建立在SpringBoot(2)的基础上。
默认页面设置
先为网站设置个默认页面,这里设置为登录页面。
方案1:controller里添加一个”/”的映射路径
@RequestMapping("/")
public String index(Model model, HttpServletResponse response) {
//model.addAttribute("name", "simonsfan");
return "/login.html";
}
方案2:设置默认的View跳转页面
@Configuration
public class DefaultView extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login.html");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
super.addViewControllers(registry);
}
}
如上两种方式均可实现在springboot中设置默认页面跳转
设置热部署模式
此处设置比较简单,查一下就知道了
一、后端
1、bean
2、dao
3、shiro
configure+Realm
二、前端
三、运行
(1)身份验证
1、浏览器中输入访问index页面
This application has no explicit mapping for /error, so you are seeing this as a fallback.
于是修改了配置文件,增加了spring.view的配置,浏览器成功定向至login页面
2、login.html页面中引用static下面的jquery-3.3.1.js一直报错,最后没办法就先把js复制到templates下面了,此时猜想可能是被shiro拦截了【后续参数传递时解决,通过指定static静态资源匹配解决】。
修改后成功调用后端checkLogin()。此时注意到此次参照的案例和之前使用shiro的模式不一样,以前是在后台显式调用shiro的login()方法,从而调用起自定义Realm验证方法。此次是在调用这个方法的过程中,被shiro拦截,然后做login验证。但是过程中并未看到自定义Realm的调用。
3、参照的例子中使用form表单提交页面数据,action路径可省略,或者填写”/login”,可以自动调用到shiro的login()验证,走进自定义realm验证逻辑中。换成其他的路径竟然不能实现。。。【后来使用手动调用login实现,该问题和下面卡住的问题是一个】。
替换方案是前端调用到后端的方法后,取得Subject实例,然后手动调用login验证方法,这个是完全可以实现的。
4、目前姑且认为调用没问题,往下走
调用到自定义Realm认证方法时,Dao层service调用时报空指针异常。记得之前刚开始用spring整合shiro的时候就报错过。查看下记录,异常原因是一致的。给ServiceImpl增加@Service注解,Service中使用的Dao层mapper增加@Resource注解,重新调用。(此时Dao层mapper增加注解后,报bean不存在的红线)【后来在mapper中增加@Component注解就好了】
5、重新登录,可以测试出账号不存在,密码不正确的异常。请求到达后台之后,直接调用自定义Realm的doGetAuthenticationInfo()验证身份,通过Dao层接口调用,验证用户名及密码。
密码验证问题:
用户信息是实现存进库中的,此时使用测试用户名,进行手动盐值加密,将值更新进数据库中。再次登录,页面跳转至index界面。
6、问题来了 ,index界面并未显示出来,不过shiro的成功登录设置生效了,且必须先登录(403页面竟然也需要先登录,明明设置了无需权限的)。
查找解决办法时,注意到登录成功的页面设置问题,说是在shiro配置中设置的路径会被更改,需重新设置。方法为:先是继承FormAuthenticationFilter,实现onLoginSuccess(),并添加到shiroConfig配置中。但是执行后效果和先前一样,都是跳转了但是没有显示页面。猜想应该就是页面访问的问题。
此时还伴随着一个问题,前端点击登录后,不再进入自定义Realm验证方法。网上看到了好多博客但是都没有解决问题。
7、卡了很久也没找到办法,那就换一种实现吧。使用Subject手动调用login()实现。
实现:
—> login界面不变,使用form,postt提交表单
—> 后端login()中,获取Subject,调用login(),走进自定义Realm身份验证中
—>从库中取得用户信息,生成用户认证Info
—>Info不为空,连同token进入CredentialsMatcher中,调用自定义doCredentialsMatch()
—>验证成功,返回index
—>前端显示index界面
到这里,觉得还是手动触发验证比较好,看得还清楚。
(2)权限认证
1、页面间参数传递
此时使用到jquery,之前出现的js引用问题在这里继续继续跟踪。参考链接
属性配置以及增加配置类都可以解决该问题。
参数传递这里想的是使用前端的cookie或者是使用Session会话中的属性。
session实现:
<1>login.html中按钮点击事件中将用户名置入sessionStorage中
sessionStorage.setItem("username",username)
<2>index.html中从sessionStorage中获取用户名
var username = sessionStorage.getItem("username")
cookie实现
用户经shiro认证,成功登录系统,进入index界面。index界面中添加了增删改查四个按钮,按钮权限由后台经过权限认证实现。
2、权限认证
查询所有人员列表,定位user角色可用,其余按钮均由admin角色可用。
查询
点击查询按钮后,首先调用权限检查方法,进入Realm权限认证方法中。调用service查询用户具备的角色,报错信息如下:
There is no getter for property named 'user' in 'class com.example.bean.SysUser'
此报错其实是因为我在Dao层直接编写了个根据整个User对象查询具备的Role列表,下面还有一处根据整个Role对象查询具备的权限列表。没更好的办法,我将两处各自修改为对应的主键查询。
在库中增加对应的角色信息,在Controller的校验方法中调用hasRole方法后,得到用户的角色、权限信息。
此时验证正确的响应没有返回到前端页面。排查原因,是因为返回的信息和ajax的dataType不一致,导致响应无法解析。
进入success分支,跳转至人员列表查询界面,报错,没找到页面
No mapping found for HTTP request with URI [/userList.html] in DispatcherServlet with name 'dispatcherServlet'
在Controller中增加了一个对应的mapping方法,然后手动返回userList.html,竟然成功了。
输入用户名,点击查询按钮,调用后端用户列表接口,前端反显结果列表。但是此时的查询Sql较为简单,想着什么办法可以拼接自定义条件进去。查了资料,比较简单的实现方式如下
@Select("<script>" +
"select \n" +
"id,username," +
"case when locked='0' then '正常'\n" +
"when locked='1' then '锁定' \n" +
"end as locked \n" +
"from sys_users " +
"where 1=1 " +
"<if test=\" #{param.username} != null \"> and username like concat(#{param.username},'%')</if>" +
"</script>")
List<SysUser> getUserList(@Param("param") Map<String,Object> param);
select语句前后加script,方法中参数前加注解,如果是传入多个参数,各自加注解即可。
此时前端页面多次查询成功页面不刷新,这里就简单调用了一下html(“”)
增加
删除
修改
上述过程是在代码中写死的关于角色,权限的限制,看到网上有直接在方法上添加所需权限、角色的注解实现这个过程的。于是操作了一把,发现注解不生效。查找解决办法,说是shiro-AOP没起作用。
shiroconfiguration中增加
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
= new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
在controller中的方法前加上注解
@RequiresPermissions("userInfo:test")
若还没生效,应该是aop没起作用,
解决方法一
shiroconfiguration中增加
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
解决方法二
pom.xml中加入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
同时application.properties中补充
spring.aop.proxy-target-class=true
两种方法分别试了一下,都可以解决问题。成功进入权限认证方法中,无对应角色或权限则抛出异常。
Request processing failed; nested exception is org.apache.shiro.authz.UnauthorizedException: Subject does not have role [test]] with root cause
对于该异常的处理,需要做拦截,让其无权限的情况下进入403页面。
@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"
return r;
}
加上此配置之后,异常不再抛出,但是页面仍然没有跳转。此问题和先前设置的无权限403页面应该属于一个问题。觉得前端ajax的调用可以修改下。这个问题下次来更新。
解决:
这篇写了好几天,问题不断,很是折腾。后续来解决问题。