1.快速入门
1.1 安装依赖
创建springboot项目,在pom.xml配置如下依赖,注:ssm整合springsecurity可见文章:https://www.cnblogs.com/fqh2020/p/11973856.html
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</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-test</artifactId> <scope>test</scope> </dependency>
1.2 编写controller
新建一个测试controller
package com.gh.springsecuritydemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Author Eric * @Date 2021/7/4 19:15 * @Version 1.0 */ @RestController @RequestMapping("admin") public class TestController { @GetMapping("hello") public String test1(){ return "hello security"; } }
1.3 启动项目
当启动项目的时候,spring security 会默认为我们生成一个password 并打印在控制台,例如:
Using generated security password: db83be86-bc15-4470-83ac-d56679a05924
访问url 时spring security 会默认进行拦截并跳转到它的登录界面
这时我们用spring security默认提供的用户名 user 和自动生成的密码进行登录,就可以访问到对应的页面了
2.设置登录的用户名和密码
2.1 通过配置文件
spring.security.user.name=root
spring.security.user.password=root
2.2 通过配置类
package com.gh.springsecuritydemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; /** * @Author Eric * @Date 2021/7/4 22:45 * @Version 1.0 */ @Configuration public class SpringsecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception{ BCryptPasswordEncoder bCryptPasswordEncoder=new BCryptPasswordEncoder(); String password = bCryptPasswordEncoder.encode("123"); auth.inMemoryAuthentication().withUser("tom").password(password).roles("admin"); } @Bean PasswordEncoder password(){ return new BCryptPasswordEncoder(); } }
2.3 自定义编写实现类
第一步:创建配置类,设置使用哪个userDetailsService实现类
package com.gh.springsecuritydemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; /** * @Author Eric * @Date 2021/7/4 23:09 * @Version 1.0 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception{ auth.userDetailsService(userDetailsService).passwordEncoder(password()); } @Bean PasswordEncoder password(){ return new BCryptPasswordEncoder(); } }
第二步:编写实现类,返回User对象,User对象有用户名密码和操作权限
package com.gh.springsecuritydemo.service; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import java.util.List; /** * @Author Eric * @Date 2021/7/4 23:14 * @Version 1.0 */ @Service("userDetailsService") public class MyUserDetailService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("role");//权限 return new User("mary", new BCryptPasswordEncoder().encode("123"), auth); } }
3.整合mybatisplus
3.1 导入依赖
<!-- mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
3.2 编写配置
# mysql数据库连接 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/demo?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=root
3.3 修改service层
启动类上加上注解@MapperScan
package com.gh.springsecuritydemo.service; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.gh.springsecuritydemo.enetity.Users; import com.gh.springsecuritydemo.mapper.UsersMapper; import lombok.Data; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import java.util.List; /** * @Author Eric * @Date 2021/7/4 23:14 * @Version 1.0 */ @Service("userDetailsService") @RequiredArgsConstructor public class MyUserDetailService implements UserDetailsService { private final UsersMapper usersMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //根据用户名查询数据 QueryWrapper<Users> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("username", username); Users user = usersMapper.selectOne(queryWrapper); if (user == null) { throw new UsernameNotFoundException("用户名不存在"); } List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("role");//权限 return new User(user.getUsername(), new BCryptPasswordEncoder().encode(user.getPassword()), auth); } }
4.自定义登录界面
4.1 配置登录页
在配置类重写configure方法
@Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() //自定义自己编写的登录页面 .loginPage("/login.html") .loginProcessingUrl("/user/login") //登录访问路径 .defaultSuccessUrl("/admin/index").permitAll() //登录成功跳转的路径 .and().authorizeRequests() .antMatchers("/","/admin/hello","/user/login").permitAll() //设置哪些路径可以直接访问,不需要认证 .anyRequest().authenticated() //所有请求都可以访问 .and().csrf().disable(); //关闭csrf防护 }
4.2 编写登录页
无需编写action对应url
5.基于角色或权限进行访问控制
5.1 hasAuthority方法
hasAuthority只针对某一个权限
在配置类设置当前地址有哪些权限可以访问
5.2 hasAnyAuthority方法
hasAnyAuthority可以匹配多个权限
用逗号进行分隔权限
5.3 hasRole方法
如果用户给定角色允许访问,否则出现403
如果当前用户具有指定角色,返回true
在service需要加上前缀ROLE_
5.4 hasAnyRole方法
该方法可以匹配多个角色
匹配
5.5 自定义没有权限访问页面
在configure中配置没有访问权限页面
//配置自定义访问权限页面 http.exceptionHandling().accessDeniedPage("/unauth.html");
6.用户授权(注解使用)
6.1 @Secured
用户具有某个角色,可以访问方法
启动类加上 @EnableGlobalMethodSecurity(securedEnabled = true) 注解,代表启动注解配置
@GetMapping("update") @Secured({"ROLE_sale","ROLE_manager"}) public String update(){ return "hello update"; }
在userDetailService设置角色
List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_sale");//权限
6.2 @PreAuthorize
该注解适合进入方法前的权限认证
@GetMapping("update") @PreAuthorize("hasAnyAuthority('sale')") public String update(){ return "hello update"; }
6.3 @PostAuthorize
在方法执行之后再进行权限验证
启动类加上 @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) 注解
@GetMapping("update") @PostAuthorize("hasAnyAuthority('sales')") public String update(){ System.out.println(111); return "hello update"; }
6.4 @PostFilter&@PreFilter
@PosterFilter 代表方法返回数据进行过滤
@PreFilter 代表方法传入数据进行过滤
7.用户退出
在配置类进行配置
//退出配置 http.logout().logoutUrl("/logout").logoutSuccessUrl("/admin/hello").permitAll();
8.自动登录
8.1 配置数据源
在配置类中进行配置
//注入数据源 @Autowired private DataSource dataSource; @Bean public PersistentTokenRepository persistentTokenRepository(){ JdbcTokenRepositoryImpl jdbcTokenRepository=new JdbcTokenRepositoryImpl(); jdbcTokenRepository.setDataSource(dataSource);//配置数据源 jdbcTokenRepository.setCreateTableOnStartup(true);//自动创建表 return jdbcTokenRepository; }
8.2 配置自动登录
8.3 修改前端登录页面
添加一个复选框,指定name必须为remember-me
9.CSRF
跨站请求伪造通常缩写为CSRF,是一种挟制用户在当前已登录的web应用程序上执行非本意的操作的攻击方法,跟跨网站脚本(XSS)相比,XSS是利用用户对指定网站的信任,CSRF利用的是网站对用户网页浏览器的信任.
跨站请求攻击,简单的说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作,(如发邮箱,发消息,转账),由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行,这利用了web身份认证的一个漏洞,简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出来的.