一、概述:

Spring 是非常流行和成功的 Java 应用开发框架,Spring Security 正是 Spring 家族中的
成员。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方
案。
正如你可能知道的关于安全方面的两个主要区域是“认证”和“授权”(或者访问控
制),一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权
(Authorization)两个部分,这两点也是 Spring Security 重要核心功能。
(1)用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问
该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认
证过程。通俗点说就是系统认为用户是否能登录
(2)用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户
所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以
进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的
权限。通俗点讲就是系统判断用户是否有权限去做某些事情。

二、SpringSecurity的特点:

◼ 和 Spring 无缝整合。
◼ 全面的权限控制。
◼ 专门为 Web 开发而设计。
◼ 旧版本不能脱离 Web 环境使用。
◼ 新版本对整个框架进行了分层抽取,分成了核心模块和 Web 模块。单独
引入核心模块就可以脱离 Web 环境。
◼ 重量级。

2.1shiro的特点:

轻量级。Shiro 主张的理念是把复杂的事情变简单。针对对性能有更高要求
的互联网应用有更好表现。
⚫ 通用性。
◼ 好处:不局限于 Web 环境,可以脱离 Web 环境使用。
◼ 缺陷:在 Web 环境下一些特定的需求需要手动编写代码定制。

三、模块划分:

image.png

四、创建项目工程:

4.1 创建一个项目:

image.png
image.png
image.png

4.2 导入相关依赖:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-security</artifactId>
  4. </dependency>

4.3 请求运行项目:

localhost:8081/test/hello
看到下面的页面:
image.png
ps:用户名默认user
密码是控制台随机密码
image.png

五、SpringSecurity基本原理:

SpringSecurity 本质是一个过滤器链:
从启动是可以获取到过滤器链:

  1. org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFil
  2. ter
  3. org.springframework.security.web.context.SecurityContextPersistenceFilter
  4. org.springframework.security.web.header.HeaderWriterFilter
  5. org.springframework.security.web.csrf.CsrfFilter
  6. org.springframework.security.web.authentication.logout.LogoutFilter
  7. org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
  8. org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
  9. org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
  10. org.springframework.security.web.savedrequest.RequestCacheAwareFilter
  11. org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
  12. org.springframework.security.web.authentication.AnonymousAuthenticationFilter
  13. org.springframework.security.web.session.SessionManagementFilter
  14. org.springframework.security.web.access.ExceptionTranslationFilter
  15. org.springframework.security.web.access.intercept.FilterSecurityInterceptor

代码底层流程:重点看三个过滤器:
FilterSecurityInterceptor:是一个方法级的权限过滤器, 基本位于过滤链的最底部。
image.png
super.beforeInvocation(fi) 表示查看之前的 filter 是否通过。
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());表示真正的调用后台的服务。
ExceptionTranslationFilter:是个异常过滤器,用来处理在认证授权过程中抛出的异常
image.png
UsernamePasswordAuthenticationFilter :对/login 的 POST 请求做拦截,校验表单中用户
名,密码。
image.png

5.1 UserDetailsService 接口讲解:

当什么也没有配置的时候,账号和密码是由 Spring Security 定义生成的。而在实际项目中账号和密码都是从数据库中查询出来的。 所以我们要通过自定义逻辑控制认证逻辑。

如果需要自定义逻辑时,只需要实现 UserDetailsService 接口即可。接口定义如下:
image.png
返回值 UserDetails
这个类是系统默认的用户“主体”

  1. // 表示获取登录用户所有权限
  2. Collection<? extends GrantedAuthority> getAuthorities();
  3. // 表示获取密码
  4. String getPassword();
  5. // 表示获取用户名
  6. String getUsername();
  7. // 表示判断账户是否过期
  8. boolean isAccountNonExpired();
  9. // 表示判断账户是否被锁定
  10. boolean isAccountNonLocked();
  11. // 表示凭证{密码}是否过期
  12. boolean isCredentialsNonExpired();
  13. // 表示当前用户是否可用
  14. boolean isEnabled();

以下是 UserDetails 实现类
image.png
以后我们只需要使用 User 这个实体类即可!

5.2 自定义登录用户名和密码的三种方式:

5.2.1 配置application.properties配置文件:

  1. server.port=8081
  2. spring.security.user.name=tang
  3. spring.security.user.password=tang

5.2.2 自定义类 继承WebSecurityConfigurerAdapter类:

  1. @Configuration
  2. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  3. @Override
  4. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  5. //用于密码加密
  6. BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
  7. String pass = passwordEncoder.encode("123456");
  8. auth.inMemoryAuthentication().withUser("lucy").password(pass).roles("admin");
  9. }
  10. @Bean
  11. PasswordEncoder password(){
  12. return new BCryptPasswordEncoder();
  13. }
  14. }

5.2.3 自定义配置信息:

5.2.3.1 创建配置类,设置使用哪一个userDetailsService实现类:

  1. @Configuration
  2. public class SecurityConfigTest extends WebSecurityConfigurerAdapter {
  3. @Autowired
  4. UserDetailsService userDetailsService;
  5. @Override
  6. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  7. auth.userDetailsService(userDetailsService).passwordEncoder(password());
  8. }
  9. @Bean
  10. PasswordEncoder password(){
  11. return new BCryptPasswordEncoder();
  12. }
  13. }

5.2.3.2 编写实现类,返回user用户对象,User对象有用户名和用户权限:

  1. @Service("userDetailsService")
  2. public class MyUserDetailsService implements UserDetailsService {
  3. @Override
  4. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  5. List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
  6. return new User("tang",new BCryptPasswordEncoder().encode("123456"),auth);
  7. }
  8. }

六、实现数据库认证来完成用户登录:

6.1 创建数据库

6.2 添加pom相关依赖

  1. <dependencies>
  2. <!-- <dependency>-->
  3. <!-- <groupId>org.springframework.boot</groupId>-->
  4. <!-- <artifactId>spring-boot-starter-jdbc</artifactId>-->
  5. <!-- </dependency>-->
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-security</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  13. </dependency>
  14. <dependency>
  15. <groupId>org.springframework.boot</groupId>
  16. <artifactId>spring-boot-starter-web</artifactId>
  17. </dependency>
  18. <dependency>
  19. <groupId>org.thymeleaf.extras</groupId>
  20. <artifactId>thymeleaf-extras-springsecurity5</artifactId>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.projectlombok</groupId>
  24. <artifactId>lombok</artifactId>
  25. <optional>true</optional>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-starter-test</artifactId>
  30. <scope>test</scope>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework.security</groupId>
  34. <artifactId>spring-security-test</artifactId>
  35. <scope>test</scope>
  36. </dependency>
  37. <!--mybatis-plus-->
  38. <dependency>
  39. <groupId>com.baomidou</groupId>
  40. <artifactId>mybatis-plus-boot-starter</artifactId>
  41. <version>3.2.0</version>
  42. </dependency>
  43. <!--mysql-->
  44. <dependency>
  45. <groupId>mysql</groupId>
  46. <artifactId>mysql-connector-java</artifactId>
  47. </dependency>
  48. <!--lombok 用来简化实体类-->
  49. <dependency>
  50. <groupId>org.projectlombok</groupId>
  51. <artifactId>lombok</artifactId>
  52. </dependency>
  53. </dependencies>

6.3 创建用户实体类

  1. @Data
  2. public class Users {
  3. private Integer id;
  4. private String username;
  5. private String password;
  6. }

6.4 配置Mybatis-plas连接信息 application.properties配置文件

  1. #mysql 数据库连接
  2. spring.datasource.driver- - class- - name= com.mysql.cj.jdbc.Driver
  3. spring.datasource.url= jdbc:mysql://localhost:3306/demo?serverTimezone=GMT%2B8
  4. spring.datasource.username= root
  5. spring.datasource.password=

6.5 创建自定义mapper类 继承接口BaseMappe<查询对应的实体类(Users)>

  1. @Repository
  2. public interface UsersMapper extends BaseMapper<Users> {
  3. }

6.6 在security的配置类中配置查询到的信息

  1. @Service( "userDetailsService")
  2. public class MyUserDetailsService implements UserDetailsService {
  3. //注入自定义的mapper
  4. @Autowired
  5. private UsersMapper usersMapper;
  6. @Override
  7. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  8. //参数中的username代表登录页面传入的用户名
  9. //配置查询的条件
  10. QueryWrapper<Users> wrapper = new QueryWrapper();
  11. //username是数据库中的字段名
  12. wrapper.eq( "username",username);
  13. //查询一条数据
  14. Users users = usersMapper.selectOne(wrapper);
  15. if(users == null) {
  16. throw new UsernameNotFoundException(" " 用户名不存在!" ");
  17. }
  18. System. out .println(users);
  19. //添加权限
  20. List<GrantedAuthority> auths =
  21. AuthorityUtils. commaSeparatedStringToAuthorityList ( "role");
  22. //返回认证的用户对象
  23. return new User(users.getUsername(),
  24. new BCryptPasswordEncoder().encode(users.getPassword()),auths);
  25. }
  26. }

七、配置自定义登录页面

7.1 在securityTest配置类中添加配置的信息

  1. //自定义配置类第一步
  2. //创建配置类,设置使用哪一个userDetailsService实现类
  3. @Configuration
  4. public class SecurityConfigTest extends WebSecurityConfigurerAdapter {
  5. @Autowired
  6. UserDetailsService userDetailsService;
  7. //注册配置的service信息
  8. @Override
  9. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  10. auth.userDetailsService(userDetailsService).passwordEncoder(password());
  11. }
  12. @Bean
  13. PasswordEncoder password(){
  14. return new BCryptPasswordEncoder();
  15. }
  16. //配置默认登录页面和请求的信息
  17. @Override
  18. protected void configure(HttpSecurity http) throws Exception {
  19. http.formLogin()//自定义自己编写的登录页面
  20. .loginPage("/login.html") //登录页面设置
  21. .loginProcessingUrl("/user/login") //配置登录访问路径
  22. .defaultSuccessUrl("/test/index").permitAll() //登录成功之后,跳转路径
  23. .and().authorizeRequests()
  24. .antMatchers("/","/test/hello","/user/login").permitAll() //设置哪些路径可以直接访问,不需要认证
  25. .anyRequest().authenticated()
  26. .and().csrf().disable();//关闭csrf防护
  27. }
  28. }

7.2 配置MyUserDetailsService类 实现接口UserDetailsService类

  1. //第二步,编写实现类,返回user用户对象,User对象有用户名和用户权限
  2. @Service("userDetailsService")
  3. public class MyUserDetailsService implements UserDetailsService {
  4. //注入UserMapper用于查询数据库
  5. @Autowired
  6. private UserMapper userMapper;
  7. @Override
  8. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  9. //调用usermapper里面的方法,根据用户名进行查询数据库
  10. // QueryWrapper<User> wrapperrap = new QueryWrapper<>();
  11. // wrapperrap.eq("adminName",username);
  12. // User user = userMapper.selectOne(wrapperrap);
  13. //权限集合
  14. List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
  15. return new User("tang",new BCryptPasswordEncoder().encode("123456"),auth);
  16. }
  17. }

7.3 在resorce中的static文件中编写login.html登录页面

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>登录</title>
  6. </head>
  7. <body>
  8. <form action="/user/login" method="post">
  9. 用户名:<input type="text" name="username">
  10. <br>
  11. 密码:<input type="text" name="password">
  12. <br>
  13. <input type="submit" value="login"/>
  14. </form>
  15. </body>
  16. </html>

八、权限方案-基于角色或权限进行访问控制

8.1 第一个方法:hasAuthority() 方法(只能包含一个权限)

ps:如果当前类的主体具有指定的权限,则返回true,否则返回false

8.1.1 在配置类设置当前访问地址有哪些权限

  1. //当前登录用户,只能具有admins权限才可以访问该路径
  2. .antMatchers("/test/index").hasAuthority("admins")

8.1.2 在UserDetailsService类中,把返回的User对象设置权限

  1. @Override
  2. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  3. //调用usermapper里面的方法,根据用户名进行查询数据库
  4. // QueryWrapper<User> wrapperrap = new QueryWrapper<>();
  5. // wrapperrap.eq("adminName",username);
  6. // User user = userMapper.selectOne(wrapperrap);
  7. //权限集合 这行代码
  8. List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("admins");
  9. return new User("tang",new BCryptPasswordEncoder().encode("123456"),auth);
  10. }

8.2 第二个方法:hasAnyAuthority()方法(可以包含多个权限)

ps:如果当前主体有任何提供的角色(给定的作为一个逗号分隔的字符串列表)的话,返回true

8.2.1 在配置类设置当前访问地址有哪些权限

  1. //当前登录用户,只能具有admins权限才可以访问该路径
  2. .antMatchers("/test/index").hasAnyAuthority("admins,manager")

8.2.2 在UserDetailsService类中,把返回的User对象设置权限

  1. //权限集合 这行代码
  2. List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("admins");

8.3 第三个方法:hasRole方法

ps:如果当前主体具有指定的角色,则返回true

8.4 第四个方法:hasAnyRole方法