springsecurity的两种认证方式
httpBasic模式 header里面带一个authenication 他使用的是BasicAuthenticationFilter
formLogin模式 表单登录用户密码 他使用的是UserPasswordAuthenticationFilter
下面我首先讲的是表单登录模式
1.简单的密码登录表单模式 (入门体验)
我使用的是springboot 2.2.4 , spring-security 5.x
引入依赖
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-security</ artifactId>
</ dependency>
这时候去访问 你的接口就是 要密码的
密码在控制台打印了,每次启动都是不一样,用户名是user
1.1 创建自己的用户密码
springsecurity配置类
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
@Bean
public UserDetailsService userDetailsService ( ) {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager ( ) ;
manager. createUser ( User. withUsername ( "lzj" ) . password ( "123456" ) . build ( ) ) ;
return manager;
}
@Bean
public PasswordEncoder passwordEncoder ( ) {
return new MyPasswordEncoder ( ) ;
}
}
解码器
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode ( CharSequence rawPassword) {
return rawPassword. toString ( ) ;
}
@Override
public boolean matches ( CharSequence rawPassword, String encodedPassword) {
return encodedPassword. equals ( rawPassword) ;
}
}
1.2 安全机制的配置
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
. . . . . . .
@Override
protected void configure ( HttpSecurity http) throws Exception {
http. authorizeRequests ( )
. antMatchers ( "/test" ) . permitAll ( )
. anyRequest ( ) . authenticated ( ) ;
}
}
重写方法
作用
configure(HttpSecurity http)
安全拦截机制,关于HTTP请求安全处理
configure(AuthenticationManagerBuilder auth)
身份验证管理生成器
configure(WebSecurity web)
WEB安全
关于安全拦截机制更多的参数配置
authorizeRequests() 认证请求对象
方法
作用
antMatchers()
匹配路径
hasRole()
拥有该角色
hasAnyRole()
拥有任意一个角色
hasAuthority()
拥有权限
hasAnyAuthority()
拥有任意一个权限
permitAll()
允许通过,放行(无条件放行)
denyAll()
无条件拒绝访问
authenticated()
需要认证
anyRequest()
任意的请求,这个要在antMatchers下面否则报错
anonymous()
允许匿名用户访问(只要认证了,不管你的授权身份)
access()
给定的SpEl表达式为true,就允许访问
hasIpAddress
如果来自给定个的IP地址就允许访问
fullyAuthenticated()
如果用户是完整认证(不是通过Remember-Me功能认证的)的话就允许访问
formLogin() 表单登录对象
方法
作用
loginPage()
登录页面默认是/login
loginProcessingUr()
设置表单填写的action地址,默认是/login
defaultSuccessUrl ()
它会默认跳转到 Referer 来源页面,如果 Referer 为空,没有来源页,则跳转到默认设置的页面。注意他要求跳转的接口必须是GET方法 ,如果你里面参数设置true,效果和successForwardUrl 一样
successForwardUrl ()
表示不管你是从哪里来的,登录后一律跳转到 successForwardUrl 指定的地址,注意他要求跳转的接口必须是POST方法(不建议使用这个,因为defaultSuccessUrl已经包含了它)
failureUrl()
登录失败转发接口
successHandler()
登录成功,的处理方式(前面都是登录成功后返回一个页面,这个是自定义返回JSON也行)这个处理类要继承SimpleUrlAuthenticationSuccessHandler
failureHandler()
登录失败,的处理方式(前面都是登录失败后返回一个页面,这个是自定义返回JSON也行)这个处理类要继承SimpleUrlAuthenticationFailureHandler
csrf() 跨域对象
方法
作用
disabl()
关闭csrf跨域访问保护
rememberMe() 记住密码功能(Remenber-Me功能)
在安全配置中开启后,只要你传有一个remember-me的参数为true的就会触发 记住密码功能
方法
作用
rememberMeParameter()
前端传的记住密码的参数名
rememberMeCookieName()
存储cookie的名字
rememberMeCookieDomain()
设置可以获取此cookie的域(IP)
tokenValiditySeconds()
存cookie有效时间(秒)
tokenRepository()
指定记住登录信息所使用的数据源,因为在客户端的cookie存的是一串无意义的秘钥,你可以在数据库存一份,对应用户的身份信息
sessionManagement() 会话管理对象
方法
作用
sessionCreationPolicy()
会话管理机制
logout() 登出对象
方法
作用
logoutUrl()
登出处理接口,一般不推荐,因为已经实现了无需自己实现,默认 /logout地址
logoutSuccessUrl()
成功登出访问的接口
logoutSuccessHandler()
成功登出的处理器,与logoutSuccessUrl二选一
logoutHandler()
不管有没有成功登出的处理器,与logoutSuccessUrl二选一
invalidateHttpSession()
是否在退出时让HttpSession无效,默认为true
2.SpringSecurity的过滤器链
2.1.SecurityContextPersistenceFilter :
在请求开始从配置好的SecurityContextRepository,并请求中获取SecurityContext,然后把他设置SecurityContextHolder,在请求完成,把SecurityContextHolder持SecurityContext保存配置好到SecurityContextRepository,并清除SecurityContextHolder所持有的SecurityContext
2.2.UsernamePasswordAuthenticationFilter :
用于处理来自表单提交的认证,该表单必须提供对应的用户名和密码,其内部的登录成功或者失败都交由AuthenticationSuccessHandler和AuthenticationFailureHandler
2.3.FilterSecurityInterceptor :
用于保护web资源的,前面的完成认证后 它将交AccessDecisionManager进行授权认证
2.4.ExceptionTransiationFilter :
能够捕获SpringSecurityFilterChain所有的异常,但他只会处理两类异常:AuthenicationException和AccessDeniedException,其他异常将会继续抛出
3.认证流程
4.自定义表单的连接数据库认证(md5+盐)
在之前我们都是在内存设定个了一个账户去认证的,现在我们开始连接数据库去认证(这里我使用的是JdbcTemplate)
WebSecurityConfig.java
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder ( ) {
return MyPasswordEncoder. getInstance ( ) ;
}
@Override
protected void configure ( HttpSecurity http) throws Exception {
http
. formLogin ( )
. loginPage ( "/index" )
. loginProcessingUrl ( "/login" )
. usernameParameter ( "username" )
. passwordParameter ( "password" )
. defaultSuccessUrl ( "/loginSuccessPage" , true )
. permitAll ( )
. and ( )
. logout ( ) . logoutUrl ( "/logout" )
. and ( )
. rememberMe ( )
. rememberMeParameter ( "remember" )
. rememberMeCookieName ( "user" )
. tokenValiditySeconds ( 60 * 60 * 24 * 7 )
. and ( )
. authorizeRequests ( )
. antMatchers ( "/test" ) . permitAll ( )
. anyRequest ( ) . authenticated ( )
. and ( )
. csrf ( )
. disable ( ) ;
}
}
MyPasswordEncoder 编码器
public class MyPasswordEncoder implements PasswordEncoder {
private String salt;
private static MyPasswordEncoder instance = new MyPasswordEncoder ( ) ;
public static MyPasswordEncoder getInstance ( ) {
return instance;
}
public void setSalt ( String salt) {
this . salt = salt;
}
@Override
public String encode ( CharSequence rawPassword) {
return Hashing. md5 ( ) . newHasher ( ) . putString ( rawPassword+ salt, Charsets. UTF_8) . hash ( ) . toString ( ) ;
}
@Override
public boolean matches ( CharSequence rawPassword, String encodedPassword) {
return encodedPassword. equals ( encode ( rawPassword) ) ;
}
}
SecurityUser.java (认证前,认证后存储的对象)
public class SecurityUser implements UserDetails , Serializable {
private String username;
private String password;
private List< String> roles;
public SecurityUser ( String username, String password, List< String> roles) {
this . username = username;
this . password = password;
this . roles = roles;
}
@Override
public Collection< ? extends GrantedAuthority > getAuthorities ( ) {
return null;
}
@Override
public String getPassword ( ) {
return this . password;
}
@Override
public String getUsername ( ) {
return this . username;
}
public void setUsername ( String username) {
this . username = username;
}
public void setPassword ( String password) {
this . password = password;
}
@Override
public boolean isAccountNonExpired ( ) {
return true ;
}
@Override
public boolean isAccountNonLocked ( ) {
return true ;
}
@Override
public boolean isCredentialsNonExpired ( ) {
return true ;
}
@Override
public boolean isEnabled ( ) {
return true ;
}
}
UserDao.java
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public User getUserByUsername ( String username) {
String queryString = "select `password`, salt from `user` where username = ?" ;
List< User> user = jdbcTemplate. query ( queryString, new Object [ ] { username} , new BeanPropertyRowMapper < > ( User. class ) ) ;
if ( user != null && user. size ( ) == 1 ) {
return user. get ( 0 ) ;
}
return null;
}
}
UserService.java
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername ( String username) throws UsernameNotFoundException {
User user = userDao. getUserByUsername ( username) ;
if ( user != null) {
MyPasswordEncoder encoder = MyPasswordEncoder. getInstance ( ) ;
encoder. setSalt ( user. getSalt ( ) ) ;
return new SecurityUser ( username, user. getPassword ( ) , null) ;
}
throw new UsernameNotFoundException ( "用户名或密码错误!" ) ;
}
}
User.java (数据库表映射的对象)
public class User {
private String username;
private String password;
private String salt;
private boolean rememberMe;
public User ( ) { }
public User ( String username, String password) {
this . username = username;
this . password = password;
}
public String getUsername ( ) {
return username;
}
public void setUsername ( String username) {
this . username = username;
}
public String getPassword ( ) {
return password;
}
public void setPassword ( String password) {
this . password = password;
}
public String getSalt ( ) {
return salt;
}
public void setSalt ( String salt) {
this . salt = salt;
}
public boolean isRememberMe ( ) {
return rememberMe;
}
public void setRememberMe ( boolean rememberMe) {
this . rememberMe = rememberMe;
}
@Override
public String toString ( ) {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", salt='" + salt + '\'' +
", rememberMe=" + rememberMe +
'}' ;
}
}
前端简单页面
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> Title</ title>
</ head>
< body>
< form action = " /login" method = " post" >
用户名< input name = " username" /> < br/>
密码< input name = " password" /> < br/>
记住我:< input type = " checkbox" name = " remember" > < br/>
< button> 登录</ button>
</ form>
</ body>
</ html>
5.会话控制
机制
描述
always
如果没有session存在就创建一个
ifRequired
如果需要就创建一个Session,登录时(默认的机制)
never
SpringSecurity将不会创建Session,但是如果应用中其他地方创建了session,那么SpringSecurity将会使用它
stateless
SpringSecurity将绝不会创建Session,也不使用Session
配置方法上面有讲到
6.授权流程
7.连接数据库授权
SecurityUser.java
public class SecurityUser implements UserDetails , Serializable {
private String username;
private String password;
private List< String> permissions;
public SecurityUser ( ) { }
public SecurityUser ( String username, String password, List< String> permissions) {
this . username = username;
this . password = password;
this . permissions = permissions;
}
@Override
public Collection< ? extends GrantedAuthority > getAuthorities ( ) {
Set< GrantedAuthority> permissioons = new HashSet < > ( ) ;
this . permissions. forEach ( ( p) - > {
permissioons. add ( new SimpleGrantedAuthority ( p) ) ;
} ) ;
return permissioons;
}
@Override
public String getPassword ( ) {
return this . password;
}
@Override
public String getUsername ( ) {
return this . username;
}
public void setUsername ( String username) {
this . username = username;
}
public void setPassword ( String password) {
this . password = password;
}
@Override
public boolean isAccountNonExpired ( ) {
return true ;
}
@Override
public boolean isAccountNonLocked ( ) {
return true ;
}
@Override
public boolean isCredentialsNonExpired ( ) {
return true ;
}
@Override
public boolean isEnabled ( ) {
return true ;
}
}
UserService .java
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername ( String username) throws UsernameNotFoundException {
User user = userDao. getUserByUsername ( username) ;
if ( user != null) {
MyPasswordEncoder encoder = MyPasswordEncoder. getInstance ( ) ;
encoder. setSalt ( user. getSalt ( ) ) ;
return new SecurityUser ( username, user. getPassword ( ) , userDao. findPermissionsByUserId ( user. getId ( ) ) ) ;
}
throw new UsernameNotFoundException ( "用户名或密码错误!" ) ;
}
}
UserDao.java
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public User getUserByUsername ( String username) {
String queryString = "select id, `password`, salt from `user` where username = ?" ;
List< User> user = jdbcTemplate. query ( queryString, new Object [ ] { username} , new BeanPropertyRowMapper < > ( User. class ) ) ;
if ( user != null && user. size ( ) == 1 ) {
return user. get ( 0 ) ;
}
return null;
}
public List< String> findPermissionsByUserId ( Integer userId) {
String queryString = "select id,`code`,description,url from permission where id in (\n" +
"select rp.id from role2permission rp inner join user2role ur on rp.role_id = ur.role_id where ur.id = ?\n" +
")" ;
List< Permission> permissionsList = jdbcTemplate. query ( queryString, new Object [ ] { userId} , new BeanPropertyRowMapper < > ( Permission. class ) ) ;
List< String> permissions = new ArrayList < > ( ) ;
permissionsList. forEach ( ( permission - > { permissions. add ( permission. getCode ( ) ) ; } ) ) ;
return permissions;
}
}
基于url的授权
@Override
protected void configure ( HttpSecurity http) throws Exception {
http
. formLogin ( )
. loginPage ( "/index" )
. loginProcessingUrl ( "/login" )
. usernameParameter ( "username" )
. passwordParameter ( "password" )
. defaultSuccessUrl ( "/loginSuccessPage" , true )
. permitAll ( )
. and ( )
. logout ( ) . logoutUrl ( "/logout" )
. and ( )
. rememberMe ( )
. rememberMeParameter ( "remember" )
. rememberMeCookieName ( "user" )
. tokenValiditySeconds ( 60 * 60 * 24 * 7 )
. and ( )
. authorizeRequests ( )
. antMatchers ( "/admin/test1" ) . hasAuthority ( "test1" )
. antMatchers ( "/admin/test2" ) . hasAuthority ( "test2" )
. antMatchers ( "/admin/test1andtest2" ) . access ( "hasAuthority('test1') and hasAuthority('test2')" )
. antMatchers ( "/test" ) . permitAll ( )
. anyRequest ( ) . authenticated ( )
. and ( )
. csrf ( )
. disable ( ) ;
}
基于方法的授权
在WebSecurityConfig标注上该注解@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
开启对应的标注
Controller类使用控制权限注解
注意 使用@Secured,而@Secured对应的角色必须要有ROLE_前缀。使用@PreAuthorize是可以随意设置的
@GetMapping ( "/admin/test1zhu" )
@Secured ( "IS_AUTHENTICATED_ANONYMOUSLY" )
public String test1zhu ( ) {
return "test1zhu" ;
}
@GetMapping ( "/admin/test2zhu" )
@PreAuthorize ( "hasAuthority('test1')" )
public String test2zhu ( ) {
return "test1zhu" ;
}
如果想接下来 学习SpringSecurity OAuth2.0请继续看我这一篇博客转载:
https://blog.csdn.net/weixin_44012722/article/details/106032730