赞
踩
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
参考百度百科:https://baike.baidu.com/item/spring security/8831652?fr=aladdin
SpringSecurity官网:https://spring.io/projects/spring-security
Security在很多企业中作为后台角色权限框架、授权认证oauth2.0 、安全防护(防止跨站点请求)、Session攻击、非常容易融合SpringMVC使用等
admin账户 所有请求都有权限访问
userAdd账户 只能访问查询和添加订单
spring-security两种模式
formLogin模式 : 表单提交认证模式 (应用广泛)
httpBasic模式 :浏览器与服务器做认证授权
关于httpBasic模式:
在HTTP协议进行通信的过程中,HTTP协议定义了基本认证过程以允许HTTP服务器对WEB浏览器进行用户身份证的方法,当一个客户端向HTTP服务 器进行数据请求时,如果客户端未被认证,则HTTP服务器将通过基本认证过程对客户端的用户名及密码进行验证,以决定用户是否合法。客户端在接收到HTTP服务器的身份认证要求后,会提示用户输入用户名及密码,然后将用户名及密码以BASE64加密,加密后的密文将附加于请求信息中, 如当用户名为toov5,密码为:123456时,客户端将用户名和密码用“:”合并,并将合并后的字符串用BASE64加密为密文,并于每次请求数据 时,将密文附加于请求头(Request Header)中。HTTP服务器在每次收到请求包后,根据协议取得客户端附加的用户信息(BASE64加密的用户名和密码),解开请求包,对用户名及密码进行验证,如果用 户名及密码正确,则根据客户端请求,返回客户端所需要的数据;否则,返回错误代码或重新要求客户端提供用户名及密码。
RBAC权限模型:
基于角色的权限访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注。在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。在一个组织中,角色是为了完成各种工作而创造,用户则依据它的责任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色。角色可依新的需求和系统的合并而赋予新的权限,而权限也可根据需要而从某角色中回收。角色与角色的关系可以建立起来以囊括更广泛的客观情况。
httpStatus:状态码
401:未授权
403:权限不足
maven依赖:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <!-- 管理依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- SpringBoot整合Web组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- springboot整合freemarker --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <!-->spring-boot 整合security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- springboot 整合mybatis框架 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!-- alibaba的druid数据库连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <!-- 注意: 这里必须要添加, 否者各种依赖有问题 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
controller:
@Controller public class OrderController { // 首页 @RequestMapping("/") public String index() { return "index"; } // 查询订单 @RequestMapping("/showOrder") public String showOrder() { return "showOrder"; } // 添加订单 @RequestMapping("/addOrder") public String addOrder() { return "addOrder"; } // 修改订单 @RequestMapping("/updateOrder") public String updateOrder() { return "updateOrder"; } // 删除订单 @RequestMapping("/deleteOrder") public String deleteOrder() { return "deleteOrder"; } // 自定义登陆页面 @GetMapping("/login") public String login() { return "login"; } }
对于403错误码的处理:
@Controller
public class ErrorController {
@RequestMapping("/error/403")
public String error() {
return "/error/403";
}
}
config:拦截请求路径与权限的配置:
对于fromLogin登录页面的修改自定义 关闭csdrf 配置loginpage就OK了
这里查询认证用户与对应权限都是通过数据库来进行查询。
@Component @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyAuthenticationSuccessHandler successHandler; @Autowired private MyAuthenticationFailureHandler failureHandler; @Autowired private MyUserDetailService myUserDetailService; @Autowired private PermissionMapper permissionMapper; //用户认证信息 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // //设置用户账号信息和权限 // auth.inMemoryAuthentication().withUser("admin").password("123456").authorities("addOrder","showOrder","updateOrder","deleteOrder"); // //添加userAdd账号 // auth.inMemoryAuthentication().withUser("userAdd").password("123456").authorities("showOrder","addOrder"); auth.userDetailsService(myUserDetailService).passwordEncoder(new PasswordEncoder() { //对表单密码进行加密 public String encode(CharSequence rawPassword) { // System.err.println("form ..."+rawPassword); return MD5Util.encode((String)rawPassword); } //加密的密码与数据库密码进行比对 //rawPassword 表单密码 encodedPassword数据库密码 public boolean matches(CharSequence rawPassword, String encodedPassword) { System.out.println("rawPassword:"+rawPassword+",encodedPassword:"+encodedPassword); //返回true代表认证成功,false失败 return MD5Util.encode((String)rawPassword).equals(encodedPassword); } }); } //配置httpSecurity拦截资源 @Override protected void configure(HttpSecurity http) throws Exception { // http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().httpBasic(); // http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().formLogin(); //如何进行权限控制 给每一个请求路径分配一个权限名称,然后账号只要关联该名称,就可以有访问权限 // http.authorizeRequests() // //配置相关权限 // .antMatchers("/showOrder").hasAnyAuthority("showOrder") // .antMatchers("/addOrder").hasAnyAuthority("addOrder") // .antMatchers("/updateOrder").hasAnyAuthority("updateOrder") // .antMatchers("/deleteOrder").hasAnyAuthority("deleteOrder") // //自定义登录界面,不拦截登录请求 // .antMatchers("/login").permitAll() // .antMatchers("/**").fullyAuthenticated().and().formLogin().loginPage("/login") // .successHandler(successHandler) .failureHandler(failureHandler) // .and().csrf().disable(); ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests = http.authorizeRequests(); List<Permission> permissionList = permissionMapper.findAllPermission(); if (permissionList != null && permissionList.size() > 0) { permissionList.stream().forEach(permission -> { //设置权限 authorizeRequests.antMatchers(permission.getUrl()).hasAnyAuthority(permission.getPermTag()); }); } authorizeRequests.antMatchers("/login").permitAll() .antMatchers("/**").fullyAuthenticated().and().formLogin().loginPage("/login").successHandler(successHandler).failureHandler(failureHandler) .and().csrf().disable(); } // @Bean // public static NoOpPasswordEncoder passwordEncoder() { // return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); // } }
配置默认错误页面:
/** * 自定义 WEB 服务器参数 可以配置默认错误页面 */ @Configuration public class WebServerAutoConfiguration { @Bean public ConfigurableServletWebServerFactory webServerFactory() { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/error/400"); ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/error/401"); ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/error/403"); ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"); ErrorPage errorPage415 = new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "/error/415"); ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"); factory.addErrorPages(errorPage400, errorPage401, errorPage403, errorPage404, errorPage415, errorPage500); return factory; } }
数据库相关配置:
sql脚本:
/* Navicat MySQL Data Transfer Source Server : localhost Source Server Version : 50556 Source Host : localhost:3306 Source Database : rbac_db Target Server Type : MYSQL Target Server Version : 50556 File Encoding : 65001 Date: 2018-11-13 18:29:33 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for sys_permission -- ---------------------------- DROP TABLE IF EXISTS `sys_permission`; CREATE TABLE `sys_permission` ( `id` int(10) NOT NULL, `permName` varchar(50) DEFAULT NULL, `permTag` varchar(50) DEFAULT NULL, `url` varchar(255) DEFAULT NULL COMMENT '请求url', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of sys_permission -- ---------------------------- INSERT INTO `sys_permission` VALUES ('1', '查询订单', 'showOrder', '/showOrder'); INSERT INTO `sys_permission` VALUES ('2', '添加订单', 'addOrder', '/addOrder'); INSERT INTO `sys_permission` VALUES ('3', '修改订单', 'updateOrder', '/updateOrder'); INSERT INTO `sys_permission` VALUES ('4', '删除订单', 'deleteOrder', '/deleteOrder'); -- ---------------------------- -- Table structure for sys_role -- ---------------------------- DROP TABLE IF EXISTS `sys_role`; CREATE TABLE `sys_role` ( `id` int(10) NOT NULL, `roleName` varchar(50) DEFAULT NULL, `roleDesc` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of sys_role -- ---------------------------- INSERT INTO `sys_role` VALUES ('1', 'admin', '管理员'); INSERT INTO `sys_role` VALUES ('2', 'add_user', '添加管理员'); -- ---------------------------- -- Table structure for sys_role_permission -- ---------------------------- DROP TABLE IF EXISTS `sys_role_permission`; CREATE TABLE `sys_role_permission` ( `role_id` int(10) DEFAULT NULL, `perm_id` int(10) DEFAULT NULL, KEY `FK_Reference_3` (`role_id`), KEY `FK_Reference_4` (`perm_id`), CONSTRAINT `FK_Reference_4` FOREIGN KEY (`perm_id`) REFERENCES `sys_permission` (`id`), CONSTRAINT `FK_Reference_3` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of sys_role_permission -- ---------------------------- INSERT INTO `sys_role_permission` VALUES ('1', '1'); INSERT INTO `sys_role_permission` VALUES ('1', '2'); INSERT INTO `sys_role_permission` VALUES ('1', '3'); INSERT INTO `sys_role_permission` VALUES ('1', '4'); INSERT INTO `sys_role_permission` VALUES ('2', '1'); INSERT INTO `sys_role_permission` VALUES ('2', '2'); -- ---------------------------- -- Table structure for sys_user -- ---------------------------- DROP TABLE IF EXISTS `sys_user`; CREATE TABLE `sys_user` ( `id` int(10) NOT NULL, `username` varchar(50) DEFAULT NULL, `realname` varchar(50) DEFAULT NULL, `password` varchar(50) DEFAULT NULL, `createDate` date DEFAULT NULL, `lastLoginTime` date DEFAULT NULL, `enabled` int(5) DEFAULT NULL, `accountNonExpired` int(5) DEFAULT NULL, `accountNonLocked` int(5) DEFAULT NULL, `credentialsNonExpired` int(5) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of sys_user -- ---------------------------- INSERT INTO `sys_user` VALUES ('1', 'admin', '张三', '15a013bcac0c50049356b322e955035e\r\n', '2018-11-13', '2018-11-13', '1', '1', '1', '1'); INSERT INTO `sys_user` VALUES ('2', 'userAdd', '小余', '15a013bcac0c50049356b322e955035e\r\n', '2018-11-13', '2018-11-13', '1', '1', '1', '1'); -- ---------------------------- -- Table structure for sys_user_role -- ---------------------------- DROP TABLE IF EXISTS `sys_user_role`; CREATE TABLE `sys_user_role` ( `user_id` int(10) DEFAULT NULL, `role_id` int(10) DEFAULT NULL, KEY `FK_Reference_1` (`user_id`), KEY `FK_Reference_2` (`role_id`), CONSTRAINT `FK_Reference_2` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`), CONSTRAINT `FK_Reference_1` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of sys_user_role -- ---------------------------- INSERT INTO `sys_user_role` VALUES ('1', '1'); INSERT INTO `sys_user_role` VALUES ('2', '2');
实体类Entity: 用户信息表的字段都是框架底层封装好的,所以要进行对应,必须实现UserDetails接口
// 用户信息表 @Data public class User implements UserDetails { /*用户ID*/ private Integer id; /*用户名称*/ private String username; /*真实姓名*/ private String realname; /*密码*/ private String password; /*创建日期*/ private Date createDate; /*最后登录时间*/ private Date lastLoginTime; /*是否可用*/ private boolean enabled; /*是否过期*/ private boolean accountNonExpired; /*是否锁定*/ private boolean accountNonLocked; /*证书是否过期*/ private boolean credentialsNonExpired; // 用户所有权限 private List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } } // 角色信息表 @Data public class Role { private Integer id; private String roleName; private String roleDesc; } @Data public class Permission { private Integer id; // 权限名称 private String permName; // 权限标识 private String permTag; // 请求url private String url; }
mapper:
public interface UserMapper { // 查询用户信息 @Select(" select * from sys_user where username = #{userName}") User findByUsername(@Param("userName") String userName); // 查询用户的权限 @Select(" select permission.* from sys_user user" + " inner join sys_user_role user_role" + " on user.id = user_role.user_id inner join " + "sys_role_permission role_permission on user_role.role_id = role_permission.role_id " + " inner join sys_permission permission on role_permission.perm_id = permission.id where user.username = #{userName};") List<Permission> findPermissionByUsername(@Param("userName") String userName); } public interface PermissionMapper { // 查询苏所有权限 @Select(" select * from sys_permission ") List<Permission> findAllPermission(); }
AuthenticationFailureHandler 认证失败接口
AuthenticationSuccessHandler 认证成功接口
成功和失败的处理:
成功:
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
// 用户认证成功
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication auth)
throws IOException, ServletException {
System.out.println("用户登陆成功");
res.sendRedirect("/");
}
}
失败:
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse res, AuthenticationException auth)
throws IOException, ServletException {
System.out.println("用户认证失败");
res.sendRedirect("http://www.baidu.com");
}
}
MD5加密utils:
public class MD5Util { // 加盐 private static final String SALT = "xwhy"; public static String encode(String password) { password = password + SALT; MessageDigest md5 = null; try { md5 = MessageDigest.getInstance("MD5"); } catch (Exception e) { throw new RuntimeException(e); } char[] charArray = password.toCharArray(); byte[] byteArray = new byte[charArray.length]; for (int i = 0; i < charArray.length; i++) byteArray[i] = (byte) charArray[i]; byte[] md5Bytes = md5.digest(byteArray); StringBuffer hexValue = new StringBuffer(); for (int i = 0; i < md5Bytes.length; i++) { int val = ((int) md5Bytes[i]) & 0xff; if (val < 16) { hexValue.append("0"); } hexValue.append(Integer.toHexString(val)); } return hexValue.toString(); } public static void main(String[] args) { System.out.println(MD5Util.encode("123456")); } }
MyUserDetailService: 设置用户动态信息,认证用户,给用户分配权限
@Service public class MyUserDetailService implements UserDetailsService { @Autowired private UserMapper userMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userMapper.findByUsername(username); if(!Objects.isNull(user)){ List<Permission> permissionList = userMapper.findPermissionByUsername(username); if(permissionList!=null&&permissionList.size()>0){ List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); permissionList.stream().forEach(e->{ authorities.add(new SimpleGrantedAuthority(e.getPermTag())); }); user.setAuthorities(authorities); } } return user; } }
启动类:
@SpringBootApplication
@MapperScan("com.xwhy.mapper")
public class AppSecurity {
public static void main(String[] args) {
SpringApplication.run(AppSecurity.class,args);
}
}
配置文件:
application.yml
# 配置freemarker spring: freemarker: # 设置模板后缀名 suffix: .ftl # 设置文档类型 content-type: text/html # 设置页面编码格式 charset: UTF-8 # 设置页面缓存 cache: false # 设置ftl文件路径 template-loader-path: - classpath:/templates # 设置静态文件路径,js,css等 mvc: static-path-pattern: /static/** ####整合数据库层 datasource: name: test url: jdbc:mysql://127.0.0.1:3306/rbac_db username: root password: 123456 # druid 连接池 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver
相关页面:
login.ftl:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <h1>每特教育--权限控制登陆系统</h1> <form action="/login" method="post"> <span>用户名称</span><input type="text" name="username" /> <br> <span>用户密码</span><input type="password" name="password" /> <br> <input type="submit" value="登陆"> </form> <#if RequestParameters['error']??> 用户名称或者密码错误 </#if> </body> </html>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。