SpringSecurity:概念:是一个声明式的提供安全访问控制的操作的安全框架;
学习有五个阶段:
小试牛刀:springsecuirtyDemo
登堂入室:用用户名拿到用户信息,根据用户信息找到用户对应的权限字符串;
游刃有余:授权方式认证;两种方式(1.web配置 2.注解方法配置);
登峰造极:认证结果处理,授权结果处理;
走火入魔:remberer_me;
小试牛刀:springsecuirtyDemo
思路步骤:
1.导入依赖,配置类;
2.设置登录成功后返回的路径;
3.网页直接访问login即可;
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<dependencies>
<!--SpringSecurity依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--web基础依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<!--junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!-- mysql数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
</dependencies>
@RestController
public class AuthController {
//登录成功后重定向地址
@PostMapping("/loginSuccess")
public String loginSuccess(){
return "登录成功";
}
}
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//提供用户信息,这里没有从数据库查询用户信息,在内存中模拟
@Bean
public UserDetailsService userDetailsService(){
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
inMemoryUserDetailsManager.createUser(User.withUsername("zs").password("123456").authorities("admin").build());
return inMemoryUserDetailsManager;
}
//密码编码器:不加密
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
//授权规则配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() //授权配置
.antMatchers("/login").permitAll() //登录路径放行
.anyRequest().authenticated() //其他路径都要认证之后才能访问
.and().formLogin() //允许表单登录
.successForwardUrl("/loginSuccess") // 设置登陆成功页
.and().logout().permitAll() //登出路径放行 /logout
.and().csrf().disable(); //关闭跨域伪造检查
}
}
登堂入室:用用户名拿到用户信息,根据用户信息找到用户对应的权限字符串;
思路步骤:
1.连接数据库使用真实的用户名密码,删除demo里的假数据;
2.配置类实现
UserDetailsService
接口,重写里面的hander方法;
3.根据用户名拿到用户对象,根据用户对象再拿到权限字符串;
4.把权限字符串给springsecurity管理;
@Component
@Slf4j
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Autowired
private PermissionMapper permissionMapper;
//通过用户名拿到用户信息,还有权限字符串
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if(!StringUtils.hasLength(username)){
throw new MyException("用户名不可为空");
}
User user = userMapper.selectByUsername(username);
if(user==null){
throw new MyException("该用户名不存在");
}
//通过用户id拿到权限集合
List<Permission> permissions = permissionMapper.selectPermissionsByUserId(user.getId());
ArrayList<GrantedAuthority> list = new ArrayList<>();
for(int i=0;i<permissions.size();i++){
Permission permission = permissions.get(i);
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(permission.getExpression());
log.info("用户,{},可用的权限有,{}",user.getUsername(),simpleGrantedAuthority);
list.add(simpleGrantedAuthority);
}
org.springframework.security.core.userdetails.User userDetails = new org.springframework.security.core.userdetails.User(user.getUsername(),user.getPassword(),list);
return userDetails;
}
}
游刃有余:授权方式认证;两种方式(1.web配置 2.注解方法配置);
使用web配置,配置类即可,告诉SpringSecurity容器可以可以使用哪些权限;
@Override
protected void configure(HttpSecurity http) throws Exception {
//先查出所有权限;
//根据权限 找到权限字符串和资源路径
//List<Permission> permissions = permissionMapper.selectList(null);
//for (Permission permission : permissions) {
// http.authorizeRequests()
// .antMatchers(permission.getResource())
// .hasAnyAuthority(permission.getExpression());
// log.info("用户授权的权限有,{}",permission.getExpression());
//}
2.注解方法配置
1.再配置类上加注解@EnableGlobalMethodSecurity(prePostEnabled = true)
2.再需要验证的方法上加上注解 @PreAuthorize("hasAuthority('dept:list')")
@RequestMapping("/list")
@PreAuthorize("hasAuthority('dept:list')")
public String list(){
return "查询部门列表成功";
}
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
登峰造极:认证结果处理,授权结果处理;
认证结果分两种,认证失败和认证成功;
思路步骤:配置类失败实现AuthenticationFailureHandler接口;
成功实现AuthenticationSuccessHandler接口;
/** 认证失败的配置类
* @author liuliang
* @date 2022-07-10 19:19
*/
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(JSONObject.toJSONString(JSONResult.error("认证失败,请登录")));
}
}
/** 认证成功的类
* @author liuliang
* @date 2022-07-10 19:40
*/
@Component
public class MyAuthenticationSuccessHandlerSuccess implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(JSONObject.toJSONString(JSONResult.success("恭喜你登录成功!")));
}
}
授权结果处理;
1.如若授权成功则直接访问;
2.如果没有权限则返回失败结果集;
3.如果未登录访问则直接返回匿名用户不可登录;
/**
* @author liuliang 授权失败的访问类
* @date 2022-07-10 19:50
*/
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException e) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(JSONObject.toJSONString(JSONResult.error("您没有此权限!!!")));
}
}
/**匿名用户访问类
* @author liuliang
* @date 2022-07-10 19:50
*/
@Component
public class MyAccessDeniedHandlerPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(JSONObject.toJSONString(JSONResult.error("您是匿名用户,无法访问此路径!")));
}
}
走过入魔:remember_me;
页面设置记住我,可以把用户信息存入cookie,下次则不用登录即可访问接口;
思路步骤:
1.再配置类里面设置jdbctoken仓库;
2.在实现方法里面实现关联即可;
//开始配置记住我功能代码
@Autowired
private DataSource dataSource;
@Bean
public JdbcTokenRepositoryImpl jdbcTokenRepository(){
JdbcTokenRepositoryImpl repository = new JdbcTokenRepositoryImpl();
repository.setDataSource(dataSource);
//repository.setCreateTableOnStartup(true);//启动时会自动创建表
return repository;
}
http.rememberMe()
.tokenRepository(jdbcTokenRepository())
.tokenValiditySeconds(60*60)
.userDetailsService(myUserDetailsService);