一、概述:
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 环境下一些特定的需求需要手动编写代码定制。
三、模块划分:
四、创建项目工程:
4.1 创建一个项目:
4.2 导入相关依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>
4.3 请求运行项目:
localhost:8081/test/hello
看到下面的页面:
ps:用户名默认user
密码是控制台随机密码
五、SpringSecurity基本原理:
SpringSecurity 本质是一个过滤器链:
从启动是可以获取到过滤器链:
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilterorg.springframework.security.web.context.SecurityContextPersistenceFilterorg.springframework.security.web.header.HeaderWriterFilterorg.springframework.security.web.csrf.CsrfFilterorg.springframework.security.web.authentication.logout.LogoutFilterorg.springframework.security.web.authentication.UsernamePasswordAuthenticationFilterorg.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilterorg.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilterorg.springframework.security.web.savedrequest.RequestCacheAwareFilterorg.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilterorg.springframework.security.web.authentication.AnonymousAuthenticationFilterorg.springframework.security.web.session.SessionManagementFilterorg.springframework.security.web.access.ExceptionTranslationFilterorg.springframework.security.web.access.intercept.FilterSecurityInterceptor
代码底层流程:重点看三个过滤器:
FilterSecurityInterceptor:是一个方法级的权限过滤器, 基本位于过滤链的最底部。
super.beforeInvocation(fi) 表示查看之前的 filter 是否通过。
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());表示真正的调用后台的服务。
ExceptionTranslationFilter:是个异常过滤器,用来处理在认证授权过程中抛出的异常
UsernamePasswordAuthenticationFilter :对/login 的 POST 请求做拦截,校验表单中用户
名,密码。
5.1 UserDetailsService 接口讲解:
当什么也没有配置的时候,账号和密码是由 Spring Security 定义生成的。而在实际项目中账号和密码都是从数据库中查询出来的。 所以我们要通过自定义逻辑控制认证逻辑。
如果需要自定义逻辑时,只需要实现 UserDetailsService 接口即可。接口定义如下:
返回值 UserDetails
这个类是系统默认的用户“主体”
// 表示获取登录用户所有权限Collection<? extends GrantedAuthority> getAuthorities();// 表示获取密码String getPassword();// 表示获取用户名String getUsername();// 表示判断账户是否过期boolean isAccountNonExpired();// 表示判断账户是否被锁定boolean isAccountNonLocked();// 表示凭证{密码}是否过期boolean isCredentialsNonExpired();// 表示当前用户是否可用boolean isEnabled();
以下是 UserDetails 实现类
以后我们只需要使用 User 这个实体类即可!
5.2 自定义登录用户名和密码的三种方式:
5.2.1 配置application.properties配置文件:
server.port=8081spring.security.user.name=tangspring.security.user.password=tang
5.2.2 自定义类 继承WebSecurityConfigurerAdapter类:
@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//用于密码加密BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();String pass = passwordEncoder.encode("123456");auth.inMemoryAuthentication().withUser("lucy").password(pass).roles("admin");}@BeanPasswordEncoder password(){return new BCryptPasswordEncoder();}}
5.2.3 自定义配置信息:
5.2.3.1 创建配置类,设置使用哪一个userDetailsService实现类:
@Configurationpublic class SecurityConfigTest extends WebSecurityConfigurerAdapter {@AutowiredUserDetailsService userDetailsService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(password());}@BeanPasswordEncoder password(){return new BCryptPasswordEncoder();}}
5.2.3.2 编写实现类,返回user用户对象,User对象有用户名和用户权限:
@Service("userDetailsService")public class MyUserDetailsService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("role");return new User("tang",new BCryptPasswordEncoder().encode("123456"),auth);}}
六、实现数据库认证来完成用户登录:
6.1 创建数据库
6.2 添加pom相关依赖
<dependencies><!-- <dependency>--><!-- <groupId>org.springframework.boot</groupId>--><!-- <artifactId>spring-boot-starter-jdbc</artifactId>--><!-- </dependency>--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency><!--mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.2.0</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--lombok 用来简化实体类--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies>
6.3 创建用户实体类
@Datapublic class Users {private Integer id;private String username;private String password;}
6.4 配置Mybatis-plas连接信息 application.properties配置文件
#mysql 数据库连接spring.datasource.driver- - class- - name= com.mysql.cj.jdbc.Driverspring.datasource.url= jdbc:mysql://localhost:3306/demo?serverTimezone=GMT%2B8spring.datasource.username= rootspring.datasource.password=
6.5 创建自定义mapper类 继承接口BaseMappe<查询对应的实体类(Users)>
@Repositorypublic interface UsersMapper extends BaseMapper<Users> {}
6.6 在security的配置类中配置查询到的信息
@Service( "userDetailsService")public class MyUserDetailsService implements UserDetailsService {//注入自定义的mapper@Autowiredprivate UsersMapper usersMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//参数中的username代表登录页面传入的用户名//配置查询的条件QueryWrapper<Users> wrapper = new QueryWrapper();//username是数据库中的字段名wrapper.eq( "username",username);//查询一条数据Users users = usersMapper.selectOne(wrapper);if(users == null) {throw new UsernameNotFoundException(" " 用户名不存在!" ");}System. out .println(users);//添加权限List<GrantedAuthority> auths =AuthorityUtils. commaSeparatedStringToAuthorityList ( "role");//返回认证的用户对象return new User(users.getUsername(),new BCryptPasswordEncoder().encode(users.getPassword()),auths);}}
七、配置自定义登录页面
7.1 在securityTest配置类中添加配置的信息
//自定义配置类第一步//创建配置类,设置使用哪一个userDetailsService实现类@Configurationpublic class SecurityConfigTest extends WebSecurityConfigurerAdapter {@AutowiredUserDetailsService userDetailsService;//注册配置的service信息@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(password());}@BeanPasswordEncoder password(){return new BCryptPasswordEncoder();}//配置默认登录页面和请求的信息@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()//自定义自己编写的登录页面.loginPage("/login.html") //登录页面设置.loginProcessingUrl("/user/login") //配置登录访问路径.defaultSuccessUrl("/test/index").permitAll() //登录成功之后,跳转路径.and().authorizeRequests().antMatchers("/","/test/hello","/user/login").permitAll() //设置哪些路径可以直接访问,不需要认证.anyRequest().authenticated().and().csrf().disable();//关闭csrf防护}}
7.2 配置MyUserDetailsService类 实现接口UserDetailsService类
//第二步,编写实现类,返回user用户对象,User对象有用户名和用户权限@Service("userDetailsService")public class MyUserDetailsService implements UserDetailsService {//注入UserMapper用于查询数据库@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//调用usermapper里面的方法,根据用户名进行查询数据库// QueryWrapper<User> wrapperrap = new QueryWrapper<>();// wrapperrap.eq("adminName",username);// User user = userMapper.selectOne(wrapperrap);//权限集合List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("role");return new User("tang",new BCryptPasswordEncoder().encode("123456"),auth);}}
7.3 在resorce中的static文件中编写login.html登录页面
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>登录</title></head><body><form action="/user/login" method="post">用户名:<input type="text" name="username"><br>密码:<input type="text" name="password"><br><input type="submit" value="login"/></form></body></html>
八、权限方案-基于角色或权限进行访问控制
8.1 第一个方法:hasAuthority() 方法(只能包含一个权限)
ps:如果当前类的主体具有指定的权限,则返回true,否则返回false
8.1.1 在配置类设置当前访问地址有哪些权限
//当前登录用户,只能具有admins权限才可以访问该路径.antMatchers("/test/index").hasAuthority("admins")
8.1.2 在UserDetailsService类中,把返回的User对象设置权限
@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//调用usermapper里面的方法,根据用户名进行查询数据库// QueryWrapper<User> wrapperrap = new QueryWrapper<>();// wrapperrap.eq("adminName",username);// User user = userMapper.selectOne(wrapperrap);//权限集合 这行代码List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("admins");return new User("tang",new BCryptPasswordEncoder().encode("123456"),auth);}
8.2 第二个方法:hasAnyAuthority()方法(可以包含多个权限)
ps:如果当前主体有任何提供的角色(给定的作为一个逗号分隔的字符串列表)的话,返回true
8.2.1 在配置类设置当前访问地址有哪些权限
//当前登录用户,只能具有admins权限才可以访问该路径.antMatchers("/test/index").hasAnyAuthority("admins,manager")
8.2.2 在UserDetailsService类中,把返回的User对象设置权限
//权限集合 这行代码List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("admins");


