目录
1.简介
与shiro对比
添加依赖
执行流程
2.UserDetailsService
User实现类
3.PasswordEncoder
BCryptPasswordEncoder
4.自定义登录逻辑
5.自定义登录界面
6.设置请求账户和密码的参数
7.自定义登陆处理器
成功
失败
8.判断
权限判断
角色判断
ip判断
9.基于注解开发
@Secured
@PreAuthorize/@PostAuthorize
10.记住我功能
导入依赖
自定义
11.未完待续...
1.简介
Spring Security是一个Java框架,用于保护应用程序的安全性。它提供了一套全面的安全解决方案,包括身份验证、授权、防止攻击等功能。Spring Security基于过滤器链的概念,可以轻松地集成到任何基于Spring的应用程序中。它支持多种身份验证选项和授权策略,开发人员可以根据需要选择适合的方式。此外,Spring Security还提供了一些附加功能,如集成第三方身份验证提供商和单点登录,以及会话管理和密码编码等。总之,Spring Security是一个强大且易于使用的框架,可以帮助开发人员提高应用程序的安全性和可靠性。
与shiro对比
Apache Shiro是一个强大且易用的Java安全框架,能够非常清晰的处理认证、授权、管理会话以及密码加密。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
ssm+shrio
springBoot+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>
<!--test-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
执行流程
2.UserDetailsService
通过传入的用户名来判断,如果要自定义逻辑来进行判断只需要实现UserDetailsService接口,而该接口又继承了UserDetails接口
public interface UserDetails extends Serializable {/*** Returns the authorities granted to the user. Cannot return <code>null</code>.* @return the authorities, sorted by natural key (never <code>null</code>)*/Collection<? extends GrantedAuthority> getAuthorities();/*** Returns the password used to authenticate the user.* @return the password*/String getPassword();/*** Returns the username used to authenticate the user. Cannot return* <code>null</code>.* @return the username (never <code>null</code>)*/String getUsername();/*** Indicates whether the user's account has expired. An expired account cannot be* authenticated.* @return <code>true</code> if the user's account is valid (ie non-expired),* <code>false</code> if no longer valid (ie expired)*/boolean isAccountNonExpired();/*** Indicates whether the user is locked or unlocked. A locked user cannot be* authenticated.* @return <code>true</code> if the user is not locked, <code>false</code> otherwise*/boolean isAccountNonLocked();/*** Indicates whether the user's credentials (password) has expired. Expired* credentials prevent authentication.* @return <code>true</code> if the user's credentials are valid (ie non-expired),* <code>false</code> if no longer valid (ie expired)*/boolean isCredentialsNonExpired();/*** Indicates whether the user is enabled or disabled. A disabled user cannot be* authenticated.* @return <code>true</code> if the user is enabled, <code>false</code> otherwise*/boolean isEnabled();}
User实现类
这个user不是我们自定义的User类,他继承了UserDetails接口,通过客户端传入的username在去数据库匹配对应的密码,然后通过查询出来的密码与前端进行匹配
public User(String username, String password, boolean enabled, boolean accountNonExpired,boolean credentialsNonExpired, boolean accountNonLocked,Collection<? extends GrantedAuthority> authorities) {Assert.isTrue(username != null && !"".equals(username) && password != null,"Cannot pass null or empty values to constructor");this.username = username;this.password = password;this.enabled = enabled;this.accountNonExpired = accountNonExpired;this.credentialsNonExpired = credentialsNonExpired;this.accountNonLocked = accountNonLocked;this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
}
3.PasswordEncoder
public interface PasswordEncoder {/*** Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or* greater hash combined with an 8-byte or greater randomly generated salt.*/String encode(CharSequence rawPassword);//加密密码/*** Verify the encoded password obtained from storage matches the submitted raw* password after it too is encoded. Returns true if the passwords match, false if* they do not. The stored password itself is never decoded.* @param rawPassword the raw password to encode and match* @param encodedPassword the encoded password from storage to compare with* @return true if the raw password, after encoding, matches the encoded password from* storage*/boolean matches(CharSequence rawPassword, String encodedPassword);//原密码和加密密码进行匹配/*** Returns true if the encoded password should be encoded again for better security,* else false. The default implementation always returns false.* @param encodedPassword the encoded password to check* @return true if the encoded password should be encoded again for better security,* else false.*/default boolean upgradeEncoding(String encodedPassword) {return false;}}
BCryptPasswordEncoder
官方推荐的一个实现类,基于哈希算法进行加密
4.自定义登录逻辑
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//1.模拟数据库查询用户名是否存在否则抛出异常UsernameNotFoundExceptionif(!"admin".equals(username)){throw new UsernameNotFoundException("用户名不存在!");}//2.把加密过的密码进行解析String password = passwordEncoder.encode("123");return new User(username,password,AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal"));}
}
5.自定义登录界面
重定向时只能用@Controller,不能用@RestController
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic PasswordEncoder getPw(){return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()//当发现是login时认为是登录,必须和表单提供的地址一致去执行UserDetailsServiceImpl.loginProcessingUrl("/login")//自定义登录界面.loginPage("/login.html")//登录成功后必须是post请求.successForwardUrl("/toMain")//登录失败后跳转.failureForwardUrl("/toError");//认证授权http.authorizeRequests()//登录失败放行不需要认证.antMatchers("/error.html").permitAll()//登录放行不需要认证.antMatchers("/login.html").permitAll()//所有请求都被拦截类似于mvc必须登录后访问.anyRequest().authenticated();//关闭csrf防护http.csrf().disable();}
}
/*** 页面跳转* @return*/@RequestMapping("/toMain")public String toMain(){return "redirect:main.html";}/*** 页面失败跳转* @return*/@RequestMapping("/toError")public String toError(){return "redirect:error.html";}
6.设置请求账户和密码的参数
登录的表单name为username和password与UsernamePasswordAuthenticationFilter中相对应,如果我们想要修改这个属性名可以通过usernameParameter()和passwordParameter()进行修改
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
7.自定义登陆处理器
由于前后端分离项目,页面跳转大多都在web浏览器中进行,这时successForwardUrl()和failureForwardUrl()方法就会失效,所以我们就要自定义方法进行实现且不能与successForwardUrl和failureForwardUrl共存
成功
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {private String url;public MyAuthenticationSuccessHandler(String url) {this.url = url;}@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {User user = (User) authentication.getPrincipal();System.out.println(user.getAuthorities());System.out.println(user.getUsername());System.out.println(user.getPassword());//密码被保护会输出nullresponse.sendRedirect(url);}
}
失败
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {private String url;public MyAuthenticationFailureHandler(String url) {this.url = url;}@Overridepublic void onAuthenticationFailure(HttpServletRequest request,HttpServletResponse response,AuthenticationException exception) throws IOException, ServletException {response.sendRedirect(url);}
}
8.判断
权限判断
在自定义逻辑中创建的User中指定,严格区分大小写
return new User(username,password,AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal"));
.antMatchers("/main1.html").hasAnyAuthority("admin")
角色判断
return new User(username,password,AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal,ROLE_abc"));
.antMatchers("/main1.html").hasAnyRole("abc")
ip判断
localhost和127.0.0.1的ip是不一样的需要指定特定的ip地址
9.基于注解开发
在启动类中添加注解@EnableGlobalMethodSecurity(securedEnabled = true)
@Secured
在controller层中添加
@Secured("ROLE_abc")
@RequestMapping("/toMain")
public String toMain(){return "redirect:main.html";
}
@PreAuthorize/@PostAuthorize
方法或类级别的注解
@PreAuthorize("hasRole('ROLE_abc')")
PreAuthorize表达式允许ROLE开头,也可以不用ROLE开头
10.记住我功能
导入依赖
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>
自定义
在config中配置
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private DataSource dataSource;
@Autowired
PersistentTokenRepository persistentTokenRepository;
http.rememberMe().tokenValiditySeconds(60)//自定义登录逻辑.userDetailsService(userDetailsService)//持久层对象.tokenRepository(persistentTokenRepository);//底层靠jdbc实现
@Bean
public PersistentTokenRepository getPersistentTokenRepository(){JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();jdbcTokenRepository.setDataSource(dataSource);//第一次启动时建表,第二次使用时注释掉//jdbcTokenRepository.setCreateTableOnStartup(true);return jdbcTokenRepository;
}