springSecurity是spring的一个顶级项目 也是一个安全框架,可以在spring框架中直接引用。
springSecurity基于RBAC用来处理登录功能和各种权限校验。
〇、配置和运行springSecurity
导入security启动器和web启动器,写一个springBoot的启动类,可以直接在springboot项目中运行security框架。
1)配置
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
2)启动并访问
3)默认登录用户
默认的密码直接在控制台打印出来了
默认的用户名是user
3.5)修改默认用户配置
4)登录成功
没有进行任何配置,404就登录成功了。
〇点五、关闭csrf防护
csrf防护是security框架默认开启的安全机制。
每次向服务器发送请求时,响应界面中都会携带一个token字符串,再次提交表单会携带此字符串和服务器中存储的token比较。
如果没有该防护,用户登录security项目,再访问第三方网站:
这时第三方网页中内嵌有security项目的相关url请求,
用户就在无意识中跟随第三方的意识访问了security项目,
而这个访问可以是提交表单,修改数据,转账操作。
要使用csrf防护机制也很简单
只需要在界面中添加name值为_csrf 的隐藏域
每次发送请求携带此键值对就ok。
这里学习security框架时可以先行关闭csrf防护,具体操作是在全局配置调用方法 http.csrf().disable();
一、全局配置 放行登录url
1)创建配置类
WebSecurityConfigurerAdapter是默认的security框架全局配置类,
security框架调用的配置方法为该类的configure(HttpSecurity http),
修改全局配置只需要新建一个继承该类的配置类重写该方法。
2)创建登录界面 和 登录控制器
2.1)使用thymeleaf技术创建界面login.html
这里用户名默认为username
密码默认为password
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
3)在配置类中配置 表单、用户请求 的访问规则
http.formLogin() .loginProcessingUrl("/login") //用户发送登录表单请求的url,会执行登录认证 .successHandler((request, response, authentication) -> {request.getRequestDispatcher("/index").forward(request,response);}) //成功后的自定义处理 .failureForwardUrl("/404") //失败的处理 .loginPage("/myLogin"); //登录网址 http.authorizeRequests() .antMatchers("/myLogin").permitAll() //.antMatchers("拦截路径")permitAll()表示放行 .anyRequest().authenticated(); //拦截所有的请求进行登录认证,校验是否登录 http.csrf().disable(); //关闭csrf防护4)测试
二、自定义用户名密码检验规则
1)重写校验类,实现自定义校验
需要实现一个接口UserDetailsService,这个接口其实是security框架提供给我们的自定义校验的接口,
实现该接口,生成该实现类对象的话,security框架就不会使用默认的内置接口实现类对象进行校验。
2)在配置类的配置方法中配置密码解析器
三、remember-me记住我
0)security完成的接口调用
使用记住我功能登录security框架以后,关闭浏览器后直接访问该项目,无需再次登录,记住我功能在浏览器cookie中存储了一个token字符串,并在数据库中也存储该token。
再次打开浏览器,不仅会校验token值,也会根据token表中记录的用户名校验用户表,如果token值一样,但是用户表中没有查询相关用户信息仍然不通过。
1) 登录界面添加值为true,键名为remember-me的复选框
2)配置dataSource对象需要的依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
3)注入dataSource提供给security框架token表操作对象jdbcTokenRepository
4)在配置类中写一个配置对象jdbcTokenRepository完成数据库表初始化
jdbcTokenRepository对象创建了表结构,并连接数据库检验token值
@Bean public JdbcTokenRepositoryImpl jdbcTokenRepository(){ JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); jdbcTokenRepository.setDataSource(dataSource); // jdbcTokenRepository.setCreateTableOnStartup(true); return jdbcTokenRepository; }
5)设置rememberMe配置
把用户名校验对象userDetailsService和token表操作对象jdbcTokenRepository扔进去完成rememberMe配置
//配置RememberMe相关信息:
http.rememberMe().userDetailsService(userDetailsService) //调用其完成用户信息的查询
.tokenRepository(jdbcTokenRepository) ; //调用其完成token的校验
// .tokenValiditySeconds(3600*24*3) ;//设置token的有效期,单位秒
// .rememberMeParameter("wollo");//设置记住我请求数据的键名
四、session会话管理
在security框架的全局配置中:
session会话失效后使用InvalidSessionStrategy类的方法处理时总是需要加上这样一句话request.getSession(),因为所有会话无效的请求都会自动调用该方法。为了避免自循环创建一个有效的会话对象。
// 会话配置
http.sessionManagement(session ->{
// 会话失效跳转的页面
// session.invalidSessionUrl("/login");
// 会话失效处理器
session.invalidSessionStrategy(new InvalidSessionStrategy() {
@Override
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
request.getSession();
response.sendRedirect("/login");
}
});
//最大并发会话数,设置单个用户允许同时在线的最大会话数,重新登录的会话会踢掉旧的会话.
session.maximumSessions(1);
});
五、使用access表达式实行指定url访问控制
1)在ExpressionInterceptUrlRegistry类中封装好了许多常用的access表达式:
2)自己写一个access表达式
2.1)封装好的denyAll()和access("denyAll()")一样的效果。
2.2)自定义校验方法
这里access表达式调用方法时规则:
使用@符号调用方法
写bean的id及调用方法
参数名必须叫request,authentication
六、用户退出功能
1)security默认的登出url请求: /out
不需要任何配置,只需要发送请求就能实现登出功能。
2)配置类中 自定义 登出配置
//登出配置 http.logout().logoutUrl("/out") .logoutSuccessUrl("/myLogin") //成功的跳转路径 // .logoutSuccessHandler(new LogoutSuccessHandler() { // @Override // public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { // response.sendRedirect("/myLogin"); // } // }) //自定义逻辑实现成功跳转 .clearAuthentication(true) //清除登录状态 .invalidateHttpSession(true) ; //销毁session
3)踢出指定用户
3.1)配置类中创建sessionRegistry对象
3.2)控制器中实现踢出方法
public void kickOutUser(String username) { // 1.获取全部登录用户 List<Object> allPrincipals = sessionRegistry.getAllPrincipals(); // 2.遍历全部登录用户,找到要强制登出的用户 for (Object principal : allPrincipals) { UserDetails userDetail = (UserDetails) principal; if (username.equals(userDetail.getUsername())) { // 3.找到认证用户所有的会话,不包含过期会话 List<SessionInformation> sessions = sessionRegistry.getAllSessions(userDetail, false); if (null != sessions && !sessions.isEmpty()) { // 4.遍历该用户的会话,使其立即失效 for (SessionInformation session : sessions) { session.expireNow(); } } } } }
七、权限校验
1)配置类中使用access表达式实现权限校验
两个hasRole方法是不同类实现的
前者必须加ROLE_。
2)使用注解实现权限校验