目录
Spring Security认证_认证成功后的处理方式
Spring Security认证_认证失败后的处理方式
Spring Security认证_退出登录
Spring Security认证_退出成功处理器
Spring Security认证_Remember Me
Spring Security授权_RBAC
Spring Security授权_权限表设计
登录成功后,如果除了跳转页面还需要执行一些自定义代码时, 如:统计访问量,推送消息等操作时,可以自定义登录成功处理器。
1、自定义登录成功处理器
public class MyLoginSuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { // 拿到登录用户的信息 UserDetails userDetails = (UserDetails)authentication.getPrincipal(); System.out.println("用户名:"+userDetails.getUsername()); System.out.println("一些操作..."); // 重定向到主页 response.sendRedirect("/main"); } }
2、配置登录成功处理器
http.formLogin() // 使用表单登录 .loginPage("/login.html") // 自定义登录页面 .usernameParameter("username") // 表单中的用户名项 .passwordParameter("password") // 表单中的密码项 .loginProcessingUrl("/login") // 登录路径,表单向该路径提交,提交后自动执行 UserDetailsService的方法 //.successForwardUrl("/main") //登录成功后跳转的路径 .successHandler(new MyLoginSuccessHandler()) //登录成功处理器 .failureForwardUrl("/fail"); //登录失败后跳转的路径
登录失败后,如果除了跳转页面还需要执行一些自定义代码时, 如:统计失败次数,记录日志等,可以自定义登录失败处理器。
1、自定义登录失败处理器
public class MyLoginFailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { System.out.println("记录失败日志..."); response.sendRedirect("/fail"); } }
2、配置登录失败处理器
http.formLogin() // 使用表单登录 .loginPage("/login.html") // 自定义登录页面 .usernameParameter("username") // 表单中的用户名项 .passwordParameter("password") // 表单中的密码项 .loginProcessingUrl("/login") // 登录路径,表单向该路径提交,提交后自动执行UserDetailsService的方法 //.successForwardUrl("/main") //登录成功后跳转的路径 .successHandler(new MyLoginSuccessHandler()) //登录成功处理器 //.failureForwardUrl("/fail") //登录失败后跳转的路径 .failureHandler(new MyLoginFailureHandler()); //登录失败处理器 // 需要认证的资源 http.authorizeRequests() .antMatchers("/login.html").permitAll() //登录页不需要认证 .antMatchers("/fail").permitAll() //失败页不需要认证 .anyRequest().authenticated(); //其余所有请求都需要认证
在系统中一般都有退出登录的操作。退出登录后,Spring Security 进行了以下操作:
1、清除认证状态
2、销毁HttpSession对象
3、跳转到登录页面
在Spring Security中,退出登录的写法如下:
1、配置退出登录的路径和退出后跳转的路径
// 退出登录配置 http.logout() .logoutUrl("/logout") // 退出登录路径 .logoutSuccessUrl("/login.html") // 退出登录后跳转的路径 .clearAuthentication(true) //清除认证状态,默认为true .invalidateHttpSession(true); // 销毁HttpSession对象,默认为true
2、在网页中添加退出登录超链接
主页面 主页面
退出登录
我们也可以自定义退出成功处理器,在退出后清理一些数据,写法 如下:
1、自定义退出成功处理器
public class MyLogoutSuccessHandler implements LogoutSuccessHandler { @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { System.out.println("清除一些数据..."); response.sendRedirect("/login.html"); } }
2、配置退出成功处理器
// 退出登录配置 http.logout() .logoutUrl("/logout") // 退出登录路径 // .logoutSuccessUrl("/login.html") // 退出登录后跳转的路径 .clearAuthentication(true) //清除认证状态,默认为true .invalidateHttpSession(true) // 销毁HttpSession对象,默认为 true .logoutSuccessHandler(new MyLogoutSuccessHandler()); //自定义退出成功处 理器
Spring Security中Remember Me为“记住我”功能,即下次访问系统 时无需重新登录。当使用“记住我”功能登录后,Spring Security会 生成一个令牌,令牌一方面保存到数据库中,另一方面生成一个叫 remember-me 的Cookie保存到客户端。之后客户端访问项目时自动携 带令牌,不登录即可完成认证。
1、编写“记住我”配置类
@Configuration public class RememberMeConfig { @Autowired private DataSource dataSource; // 令牌Repository @Bean public PersistentTokenRepository getPersistentTokenRepository() { // 为Spring Security自带的令牌控制器设置数据源 JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl = new JdbcTokenRepositoryImpl(); jdbcTokenRepositoryImpl.setDataSource(dataSource); //自动建表,第一次启动时需要,第二次启动时注释掉 // jdbcTokenRepositoryImpl.setCreateTableOnStartup(true); return jdbcTokenRepositoryImpl; } }
2、修改Security配置类
// 记住我配置 http.rememberMe() .userDetailsService(userDetailsService)//登录逻辑交给哪个对象 .tokenRepository(repository) //持久层对象 .tokenValiditySeconds(30); //保存时间,单位:秒
3、在登录页面添加“记住我”复选框
授权即认证通过后,系统给用户赋予一定的权限,用户只能根据权 限访问系统中的某些资源。RBAC是业界普遍采用的授权方式,它有 两种解释:
Role-Based Access Control
基于角色的访问控制,即按角色进行授权。比如在企业管理系统 中,主体角色为总经理可以查询企业运营报表。逻辑为:
if(主体.hasRole("总经理角色")){
查询运营报表
}
如果查询企业运营报表的角色变化为总经理和股东,此时就需要修 改判断逻辑代码:
if(主体.hasRole("总经理角色") || 主体.hasRole("股东角色")){
查询运营报表
}
此时我们可以发现,当需要修改角色的权限时就需要修改授权的相 关代码,系统可扩展性差。
Resource-Based Access Control
基于资源的访问控制,即按资源(或权限)进行授权。比如在企业 管理系统中,用户必须 具有查询报表权限才可以查询企业运营报 表。逻辑为:
if(主体.hasPermission("查询报表权限")){
查询运营报表
}
这样在系统设计时就已经定义好查询报表的权限标识,即使查询报 表所需要的角色变化为总经理和股东也不需要修改授权代码,系统 可扩展性强。该授权方式更加常用。
用户和权限的关系为多对多,即用户拥有多个权限,权限也属于多 个用户,所以建表方式如下:
这种方式需要指定用户有哪些权限,如:张三有查询工资的权限, 即在用户权限中间表中添加一条数据,分别记录张三和查询工资权 限ID。但在系统中权限数量可能非常庞大,如果一条一条添加维护 数据较为繁琐。所以我们通常的做法是再加一张角色表:
用户角色,角色权限都是多对多关系,即一个用户拥有多个角色, 一个角色属于多个用户;一个角色拥有多个权限,一个权限属于多 个角色。这种方式需要指定用户有哪些角色,而角色又有哪些权限。
如:张三拥有总经理的角色,而总经理拥有查询工资、查询报表的 权限,这样张三就拥有了查询工资、查询报表的权限。这样管理用 户时只需管理少量角色,而管理角色时也只需要管理少量权限即可。接下来我们创建五张表:
CREATE TABLE `users` (`uid` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`uid`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; INSERT INTO `users` VALUES (1, 'xiaotong','$2a$10$Eqv9PRMl6bPt5BiwgPr2eucgyl.E.xLENt4b vfDvv7DyS5AVPT.U6', '13812345678'); CREATE TABLE `role` ( `rid` int(11) NOT NULL AUTO_INCREMENT, `roleName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `roleDesc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`rid`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; INSERT INTO `role` VALUES (1,'总经理','管理整个公司'); INSERT INTO `role` VALUES (2,'股东','参与公司决策'); INSERT INTO `role` VALUES (3,'财务','管理公司资产'); CREATE TABLE `permission` ( `pid` int(11) NOT NULL AUTO_INCREMENT, `permissionName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`pid`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; INSERT INTO `permission` VALUES (1,'查询报表', '/reportform/find'); INSERT INTO `permission` VALUES (2,'查询工资', '/salary/find'); INSERT INTO `permission` VALUES (3,'查询税务', '/tax/find'); CREATE TABLE `users_role` ( `uid` int(255) NOT NULL, `rid` int(11) NOT NULL, PRIMARY KEY (`uid`, `rid`) USING BTREE, INDEX `rid`(`rid`) USING BTREE, CONSTRAINT `users_role_ibfk_1` FOREIGN KEY(`uid`) REFERENCES `users` (`uid`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `users_role_ibfk_2` FOREIGN KEY(`rid`) REFERENCES `role` (`rid`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; INSERT INTO `users_role` VALUES (1, 2); INSERT INTO `users_role` VALUES (1, 3); CREATE TABLE `role_permission` ( `rid` int(11) NOT NULL, `pid` int(11) NOT NULL, PRIMARY KEY (`rid`, `pid`) USING BTREE, INDEX `pid`(`pid`) USING BTREE, CONSTRAINT `role_permission_ibfk_1` FOREIGN KEY (`rid`) REFERENCES `role` (`rid`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `role_permission_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `permission` (`pid`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; INSERT INTO `role_permission` VALUES (1, 1); INSERT INTO `role_permission` VALUES (2, 1); INSERT INTO `role_permission` VALUES (1, 2); INSERT INTO `role_permission` VALUES (3, 2); INSERT INTO `role_permission` VALUES (1, 3); INSERT INTO `role_permission` VALUES (2, 3);