标签:index 登录 介绍 SpringSecurity jsp 使用 security 表单
一、用法简介 用户登录系统时 我们协助 SpringSecurity 把用户对应的角色、权限装配好,同时把各个资源所要求的权限信息设定好,剩下的“登录验证”和 “权限验证”都交给SpringSecurity 二、权限管理的概念 1.主体 principal 使用系统的用户或设备或从其他系统远程登录的用户等等。简单的说就是那个使用系统那个就是主体。 2.认证 authentication 权限管理系统确认一个主体的身份。简单的来说就是证明主体自己是谁。总体来说就是我们做的登录系统 3.授权 authorization 将操作系统“权限”“授予”给主体,这样主体就具备了操作系统中特定功能的能力。简单的说就是授权就是给用户分配权限。 三、权限管理的主流框架 1.SpringSecurity spring的技术栈组成部分 通过提供完整可扩展的认证和授权支持保护应用程序 Spring Security 特点: 和spring无缝整合 完整的权限控制 专门为web开发而设计: 1.旧版本不能脱离web环境使用 2.新版本对整个框架进行了分层抽取,分成了核心模块和web模块,单独引入核心模块就可 以脱离web环境 重量级框架 2.Shiro Apache旗下的轻量级权限控制框架 特点: 轻量级。shiro主张的理念是把复杂的事情变得简单。针对性能有更高要求的互联网应用有更好的表现。 通用性 好处:不局限于web环境,可以脱离web环境使用 缺陷: 在web环境下一些特定的需求需要手动编写代码定制。 四、创建HelloWorld工程 步骤:1.创建Maven的web工程 2.加入SpringMVC需要的环境 3.创建springmvc配置文件 4.在web.xml中配置DispatcherServlet 5.创建包 6. 加入准备好的文件到Hello工程中 五、在HelloWorld工程中加入SpringSecurity 1.加入SpringSecurity依赖 < dependency > < groupId >org.springframework.security</ groupId > <artifactId>spring-security-web</artifactId> <version>5.3.10.RELEASE</version> </dependency> <!-- SpringSecurity 配置 --> <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>5.3.10.RELEASE</version> </dependency> <!-- SpringSecurity 标签库 --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> < version >5.3.10.RELEASE</ version > </ dependency > 2. 加入SpringSecurity控制权限的Filter SpringSecurity使用的是过滤器Filter而不是 拦截器Interceptor,意味着SpringSecurity能够管理的不仅仅是SpringMVC中的handler请求,还包含web应用中的所有请求。例:项目中的静态资源也会被拦截,从而进行权限控制 < filter > < filter-name >springSecurityFilterChain</ filter-name > <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </ filter-mapping > 注意:标签中必须是springSecurityFilterChain。因为springSecurityFilterChain在IOC容器中对应真正执行权限控制的二十几个Filter。只有这个名字才能加载到这些Filter 3.加入配置类 package com.pl.security.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; //注意:这个类一定要放在自动扫描的包下,不然无法生效 @Configuration //将这个类标记为配置类 @EnableWebSecurity //启用web环境下的权限控制功能 public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter { } 4.效果 所有的请求都被SpringSecurity拦截,要求登录才可以访问 静态资源也都被拦截,要求登录 登录失败也有错误提示 六 、SpringSecurity实验操作 1.放行首页和静态资源 在配置类中重写父类的configure(HTTPSecurity security)方法 @Override protected void configure(HttpSecurity security) throws Exception { security.authorizeRequests() //表示给请求授权 .antMatchers("/index.jsp") //针对/index.jsp路径进行授权 .permitAll() //可以无条件访问 .antMatchers("/layui/**").permitAll() //针对/layui目录下所有资源进行授权 都可以无条件访问 .anyRequest() // 任意请求 .authenticated() //需要登录以后才能访问 ; } 效果:未进行登录认证的请求会跳到错误403页面 2.未认证登录的请求跳转到登录页面 @Override protected void configure(HttpSecurity security) throws Exception { // super.configure(security); 注释掉取消父类中的默认规则 security.authorizeRequests() //表示给请求授权 .antMatchers("/index.jsp") //针对/index.jsp路径进行授权 .permitAll() // 可以无条件访问 .antMatchers("/layui/**").permitAll() //针对/layui目录下所有资源进行授权 都可以无条件访问 .and() .formLogin() //使用表单形式登录 //关于loginPage()方法的特殊说明 //指定登录页的同时会影响到:“提交登录表单的地址”、“退出登录的地址”、“登录失败的地址”、 // /index.jsp GET - the login form 去登录页面 // /index.jsp POST - process the credentials and if valid authenticate the user 提交登录表单 // /index.jsp?error GET - redirect here for failed authentication attempts 登录失败 // /index.jsp?logout GET - redirect here after successfully logging out 退出登录 .loginPage( "/index.jsp" ) // 指定登录页面(如果没有指定会访问默认的登录页面) //loginProcessingUrl() 方法指定登录地址,就会覆盖loginPage()方法中设定的默认值 .loginProcessingUrl( "/do/login.html" ) // 指定提交登录表单的地址 //.permitAll() //放行 ; } 3.设置登录系统的账号和密码 3.1先对页面的表单设置 <p style="text-align: center">${SPRING_SECURITY_LAST_EXCEPTION.message}</p> <form method="post" action="${pageContext.request.contextPath}/do/login.html"> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"> ........ </form> SpringSecurity 账号密码默认的请求参数名分别为 username 、 password,所以要么修改表单项中name的属性值,要么修改配置。如果修改配置可以调用 usernameParameter()和 passwordParameter()方法 3.2 设置登录成功后默认前往的页面 @Override protected void configure(HttpSecurity security) throws Exception { // super.configure(security); 注释掉取消父类中的默认规则 security.authorizeRequests() //表示给请求授权 .antMatchers("/index.jsp") //针对/index.jsp路径进行授权 .permitAll() //可以无条件访问 .antMatchers("/layui/**").permitAll() //针对/layui目录下所有资源进行授权 都可以无条件访问 .and() .authorizeRequests() //对请求进行授权 .anyRequest() //任意请求 .authenticated() //需要登录以后才能访问 .and() .formLogin() //使用表单形式登录 //关于loginPage()方法的特殊说明 //指定登录页的同时会影响到:“提交登录表单的地址”、“退出登录的地址”、“登录失败的地址”、 // /index.jsp GET - the login form 去登录页面 // /index.jsp POST - process the credentials and if valid authenticate the user 提交登录表单 // /index.jsp?error GET - redirect here for failed authentication attempts 登录失败 // /index.jsp?logout GET - redirect here after successfully logging out 退出登录 .loginPage("/index.jsp") //指定登录页面(如果没有指定会访问默认的登录页面) //loginProcessingUrl() 方法指定登录地址,就会覆盖loginPage()方法中设定的默认值 .loginProcessingUrl("/do/login.html") //指定提交登录表单的地址 //.permitAll() //登录地址本身也需要permitAll()放行 .usernameParameter("loginAcct") //定制登录账号的请求参数名 .passwordParameter("userPswd") //定制登录密码的请求参数名 .defaultSuccessUrl("/main.html") //设置登录成功后前往的地址 ; } 3.3 重写另外一个父类方法,来设置登录系统的账号和密码 @Override protected void configure(AuthenticationManagerBuilder builder) throws Exception { builder.inMemoryAuthentication() // 在内存中完成账号密码的检查 //.passwordEncoder(new MyPasswordEncoder()) //.password("123") // 指定密码 .withUser("tom") // 指定账号 .password(new BCryptPasswordEncoder().encode("123")) //指定密码 .roles("ADMIN") // 指定当前用户的角色 .authorities("SAVE","EDIT") ; } 4.用户注销 通过调用HttpSecurity对象的一系列方法设置注销功能 logout()方法:开启注销功能 logoutUrl()方法:自定义注销功能的url地址 注:如果CSRF功能没有禁用,那么退出请求方式必须是POST请求方式。如果禁用了 CSRF功能则任何请求方式都可以 logoutSuccessUrl()方法:退出成功后前往URL地址 addLogoutHandler()方法:添加退出处理器 logoutSuccessHandler()方法:退出成功处理器 退出的表单: <form id="logoutForm" action="${pageContext.request.contextPath }/do/logout.html" method="post"> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> </form> <a id="logoutAnchor" href="">退出</a> <script type="text/javascript"> window.onload = function() { //给超链接的DOM对象绑定单击响应函数 document.getElementById("logoutAnchor").onclick = function() { //提交包含CSRF参数的表单 document.getElementById("logoutForm").submit(); //取消超链接的默认行为 return false; }; }; </script> 5.基于角色或者权限进行访问控制 通过HttpSecurity对象设置资源的角色要求 security.authorizeRequests() //表示给请求授权 .antMatchers( "/index.jsp" ) // 针对 /index.jsp 路径进行授权 .permitAll() //可以无条件访问 .antMatchers( "/layui/**" ).permitAll() // 针对 /layui 目录下所有资源进行授权 都可以无条件访问 .antMatchers( "/level1/**" ) // 针对 level1/** 路径设置访问要求 .hasRole("学徒") // 要求用户具备学徒的角色才能访问 .antMatchers( "/level2/**" ) // 针对 level2/** 路径下设置访问要求 .hasAuthority("核心弟子") // 要求用户具备核心弟子的角色才能访问 .and() .authorizeRequests() // 对请求进行授权 .anyRequest() //任意请求 .authenticated() //需要登录以后才能访问 .and() .formLogin() //使用表单形式登录 .loginPage("/index.jsp") //指定登录页面(如果没有指定会访问默认的登录页面) .loginProcessingUrl("/do/login.html") //指定提交登录表单的地址 .usernameParameter("loginAcct") //定制登录账号的请求参数名 .passwordParameter("userPswd") //定制登录密码的请求参数名 .defaultSuccessUrl("/main.html") //设置登录成功后前往的地址 .and() .logout() //开启退出功能 .logoutUrl("/do/logout.html") //指定退出地址 .logoutSuccessUrl("/index.jsp") //指定退出成功去的地址 通过 AuthenticationManagerBuilder 设置登录时所具备的角色 builder.inMemoryAuthentication() // 在内存中完成账号密码的检查 .passwordEncoder(new MyPasswordEncoder()) //.password("123") // 指定密码 .withUser("tom") // 指定账号 .password("123") //指定密码 .roles("ADMIN","学徒") // 指定当前用户的角色 .and() .withUser("pl") .password("123") .authorities("SAVE","核心弟子") ; 当访问被拒绝后会看到403错误页面: 注意:要注意方法的优先级:anyRequest() 和 authenticated()要设置在 antMatchers()、hasRole()、hasAuthority()的后面,如果先调用那就会把 antMatchers()、hasRole()、hasAuthority()的设置覆盖,导致该设置无效。所以要先调用设置具体小范围,然后在调用大模糊范围设置 6.自定义403错误页面 HttpSecurity对象 .accessDeniedPage( "/to/no/auth/page.html" ) // 前往访问被拒绝的页面 .accessDeniedHandler( new AccessDeniedHandler () { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { request.setAttribute("message","=== 抱歉!你无法访问这个资源!==="); request.getRequestDispatcher("/WEB-INF/views/no_auth.jsp").forward(request,response); } }) 7.记住我-内存 版(Cookie) HttpSecurity对象调用 rememberMe()方法 将登录表单中的checkbox的name设置为remember-me 如果不使用remember-me作为请求参数名称,可以使用 rememberMeParameter()方法定制 8.记住我-数据库 版 为了让服务器重启也不影响记住我登录功能,就将用户信息存入数据库中 步骤:导入依赖 配置数据源 创建数据库 注入数据源 启动令牌仓库功能: JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl(); tokenRepository .setDataSource( dataSource ); HttpSecurity对象. tokenRepository( tokenRepository ); 注:需要进入JdbcTokenRepositoryImpl类中找到创建persistent_logins表的SQL语句建表 如下: public static final String CREATE_TABLE_SQL = "create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null)" ; 9.查询数据库完成认证登录 1.SpringSecurity默认实现 builder.jdbcAuthentication().usersByUsernameQuery( "tom" ); 在 usersByUsernameQuery( "tom" );方法中最终调用了JdbcDaoImpl类的方法查询数据库 SpringSecurity的默认实现已经将SQL语句编码在了JdbcDaoImpl类中。这种情况下有三种方式选择: 按照JdbcDaoImpl类中的SQL语句设计表结构 修改JdbcDaoImpl的源码 不使用jdbcAuthentication() 2. 自定义数据库查询方式 builder.userDetailsService( userDetailsService ) 其中 userDetailsService需要自定义实现U serDetailsService接口中的类并进行自动装配 @Autowired private JdbcTemplate jdbcTemplate; //根据表单提交的用户名查询User对象,并装配角色、权限等信息 @Override public UserDetails loadUserByUsername( //表单提交的用户名 String username) throws UsernameNotFoundException { //1.从数据库中查询User对象 String sql="SELECT id,loginacct,userpswd,username,email,createtime FROM t_admin WHERE username= ?"; List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class), username); User user = userList.get(0); //2.给User设置角色权限信息 List<GrantedAuthority> authorities=new ArrayList<>(); authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); authorities.add(new SimpleGrantedAuthority("UPDATE")); authorities.add(new SimpleGrantedAuthority("核心弟子")); //3.把user对象和authorities封装到UserDetails中 String userpaswd = user.getUserpswd(); return new org.springframework.security.core.userdetails.User(username,userpaswd,authorities); } 3. 使用自定义 U serDetailsService 完成登录 builder.userDetailsService( userDetailsService ); 10.应用自定义密码加密规则 自定义类实现 org.springframework.security.crypto.password.PasswordEncoder 类的接口 @Override public String encode(CharSequence rawPassword) { return privateEncode(rawPassword); } @Override public boolean matches(CharSequence rawPassword, String encodedPassword) { //1.对明文密码进行加密 String formPassword = privateEncode(rawPassword); //2.声明数据库密码 String dataPassword=encodedPassword; //3.比较 return Objects.equals(formPassword,dataPassword); } encode()方法是对明文密码进行加密 matches()方法是比较明文加密后的密码和密文 然后在配置类中应用加密规则 builder.userDetailsService( userDetailsService ).passwordEncoder(passwordEncoder); SpringSecurity提供了 BCryptPasswordEncoder 加密规则 BCryptPasswordEncoder 创 建 对 象 后 代 替 自 定 义 passwordEncoder 对 象 即 可 。 BCryptPasswordEncoder 在加密时通过加入随机盐值让每一次的加密结果都不同。能够 避免密码的明文被猜到。 而在对明文和密文进行比较时,BCryptPasswordEncoder 会在密文的固定位置取出 盐值,重新进行加密 测试: public void test (){ //准备明文字符串 String password="123"; //准备密文字符串 String encodePassword="$2a$10$4ZPFToIKfYIkG.nWYjrtY.I.knAqbg/KqAliwLsQrroKmL5OKUoJO"; //创建一个BCryptPasswordEncoder对象 BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); //比较 boolean matches = passwordEncoder.matches(password, encodePassword); //打印 System.out.println(matches ? "一致":"不一致"); }标签:index,登录,介绍,SpringSecurity,jsp,使用,security,表单 来源: https://blog.csdn.net/weixin_49983224/article/details/122173617
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。