赞
踩
注意,在配置过滤器时 每个权限/角色配置操作前面都要写上请求路径
如: .anyRequest() 请求路径
.authenticated()//都需要认证操作 是权限/角色操作
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
加这个的目的是每次请求时都把csrf防护生成的随机token带上
应用添加security依赖后,在application配置文件中写入spring.security.user.name和spring.security.user.password即可,若注入成功 控制台不会报security框架自动生成的初始密码
# 应用名称
spring.application.name=spring-security-smalldemo
# 应用服务 WEB 访问端口
server.port=8080
#设置用户名密码方式一 配置文件
spring.security.user.name=lql
spring.security.user.password=mima
在配置类中编写如下即可
package com.li.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 liql * @date 2021/9/21 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //密码需要加密 不然报 Encoded password does not look like BCrypt 错误 BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String password = passwordEncoder.encode("123456"); //角色不能不设置,不然报错 auth.inMemoryAuthentication().withUser("lql").password(password).roles(""); } //不注入PasswordEncoder 会报下面的错误 //java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null" @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } }
分别定义一个配置类和接口实例类即可,
用户名密码查库操作可定义在该实例中只需要替换返回对象User中的参数为数据库查到的即可。
package com.li.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; /** 设置用户名密码的第三种方式 通过实现 userDetailsService 接口 * @author liql * @date 2021/9/21 */ @Configuration public class SecurityConfig2 extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } //不注入PasswordEncoder 会报下面的错误 //java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null" @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } }
package com.li.config; 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; /** * 配置第三种用户密码的方式的实现类 UserDetailsService接口是security框架的 * @author liql * @date 2021/9/21 */ @Service("userDetailsService") public class UserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //设置权限 List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList("roles"); //第三个参数为设置权限,不能为空。 return new User("lql", new BCryptPasswordEncoder().encode("123"), authorityList); } }
参数 username为登录界面输入的用户名,把查库拿到的密码传入到返回值中,security底层会自动将查库的密码与登录界面输入的密码做校验
package com.li.config; 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; import java.util.concurrent.ConcurrentHashMap; /** * 配置第三种用户密码的方式的实现类 UserDetailsService接口是security框架的 * @author liql * @date 2021/9/21 */ @Service("userDetailsService") public class UserDetailsServiceImpl implements UserDetailsService { //定义一个安全类集合模拟数据库 private static ConcurrentHashMap<String,String> mydb=new ConcurrentHashMap(); static { mydb.put("lql", "111"); mydb.put("xiaohong", "123"); } /** * * @param username 该参数为登录界面输入的用户名 * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //设置权限 List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList("roles"); //模拟通过数据库查密码 String password=mydb.get(username); // if(查库没查到){ // throw new UsernameNotFoundException("用户名不存在") // } //把数据库查到的密码传入进去 security框架底层会帮我们自动做校验 第三个参数为设置权限,不能为空。 return new User(username, new BCryptPasswordEncoder().encode(password), authorityList); } }
在原先的配置类中重写 void: configure(HttpSecurity http) 方法
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
}
重写后
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().loginPage("/login.html") //登录界面
.loginProcessingUrl("/user/login") //用户密码需要提交到的路径 该路径由security管理 不需要我们定义
.defaultSuccessUrl("/test/index")//登录成功后跳转到的页面
.permitAll()// 无条件允许访问 不校验权限
.and()
.authorizeRequests()//后面写认证配置
.anyRequest() //任何请求
.authenticated()//都需要认证操作
// .antMatchers("/","/test/hello").permitAll() //设置哪些无条件允许访问 但必须先认证然后不校验权限
.and()
.csrf().disable();//关闭csrf防护 关闭跨域保护
}
添加依赖和配置
spring.thymeleaf.prefix=classpath:/static
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
login.html
页面 username password 参数名是固定的,因为在UsernamePasswordAuthenticationFilter过滤器中明确定义了
<!DOCTYPE html> <!-- 需要添加 <html xmlns:th="http://www.thymeleaf.org"> 这样在后面的th标签就不会报错 --> <html xmlns:th="http://www.thymeleaf.org"> <head lang="en"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>xx</title> </head> <body> <h1>表单提交</h1> <!-- 表单提交用户信息,注意字段的设置,直接是*{} --> <form action="/user/login" method="post"> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> <input type="text" name="username" /> <input type="text" name="password" /> <input type="submit" /> </form> </body> </html>
登录成功跳转页面
@GetMapping("/index")
@ResponseBody
public String index(){
return "登录成功";
}
在UserDetailsService的实例类里进行授权
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //设置权限 List<GrantedAuthority> authorityList = AuthorityUtils //该方法 commaSeparatedStringToAuthorityList 技能设置权限又能设置角色,设置角色时加前缀 ROLE_XXX , 多个权限和角色中间用逗号隔开 .commaSeparatedStringToAuthorityList("perm_hello1,perm_hello2"); //模拟通过数据库查密码 String password=mydb.get(username); // if(查库没查到){ // throw new UsernameNotFoundException("用户名不存在") // } //把数据库查到的密码传入进去 security框架底层会帮我们自动做校验 第三个参数为设置权限和角色,不能为空。 return new User(username, new BCryptPasswordEncoder().encode(password), authorityList); }
在配置类里
@Override protected void configure(HttpSecurity http) throws Exception { /* http.formLogin().loginPage("/login.html") //登录界面 .loginProcessingUrl("/user/login") //用户密码需要提交到的路径 该路径由security管理 不需要我们定义 .defaultSuccessUrl("/test/index")//登录成功后跳转到的页面 .permitAll()// 无条件允许访问 但必须先认证然后不校验权限 .and() .authorizeRequests()//后面写认证配置 .anyRequest() //任何请求 .authenticated()//都需要认证操作 // .antMatchers("/","/test/hello").permitAll() //设置哪些路径可以不用认证就能访问 .and() .csrf().disable();//关闭csrf防护 关闭跨域保护*/ http.formLogin().loginPage("/login.html") //登录界面 .loginProcessingUrl("/user/login") //用户密码需要提交到的路径 该路径由security管理 不需要我们定义 .defaultSuccessUrl("/test/index")//登录成功后跳转到的页面 .permitAll()// 无条件允许访问 但必须先认证然后不校验权限 .and() .authorizeRequests() .antMatchers("/test/hello") .hasAnyAuthority("perm_hello")//满足该权限 ,只能写单个权限 .antMatchers("/test/hello2") //满足下面权限的一个即可放行 .hasAnyAuthority("perm_hello,perm_hello2") .anyRequest() //任何请求 .authenticated()//都需要认证操作 .and() .csrf().disable();//关闭csrf防护 关闭跨域保护; }
与设置权限的地方相同 ,在实例类中 设置权限的地方增加 ROLE_xxx 即可
加“ROLE_”前缀的原因是,在配置过滤器时 如写个角色为 “admin” 但是在底层会校验“ROLE_admin”;
List<GrantedAuthority> authorityList =
AuthorityUtils
//该方法 commaSeparatedStringToAuthorityList 技能设置权限又能设置角色,设置角色时加前缀 ROLE_XXX , 多个权限和角色中间用逗号隔开
//ROLE_admin 给用户设置 admin角色
.commaSeparatedStringToAuthorityList("perm_hello1,perm_hello2,ROLE_admin");
与设置路径的地方相同 在配置类中
增加
.antMatchers("/test/hello2")
//满足下面角色的一个即可放行
.hasRole("admin")
.antMatchers("/test/hello3")
//满足其中的一个角色即可放行
.hasAnyRole("admin,admin2")
@Override protected void configure(HttpSecurity http) throws Exception { /* http.formLogin().loginPage("/login.html") //登录界面 .loginProcessingUrl("/user/login") //用户密码需要提交到的路径 该路径由security管理 不需要我们定义 .defaultSuccessUrl("/test/index")//登录成功后跳转到的页面 .permitAll()// 无条件允许访问 但必须先认证然后不校验权限 .and() .authorizeRequests()//后面写认证配置 .anyRequest() //任何请求 .authenticated()//都需要认证操作 // .antMatchers("/","/test/hello").permitAll() //设置哪些路径可以不用认证就能访问 .and() .csrf().disable();//关闭csrf防护 关闭跨域保护*/ http.formLogin().loginPage("/login.html") //登录界面 .loginProcessingUrl("/user/login") //用户密码需要提交到的路径 该路径由security管理 不需要我们定义 .defaultSuccessUrl("/test/index")//登录成功后跳转到的页面 .permitAll()// 无条件允许访问 但必须先认证然后不校验权限 .and() .authorizeRequests() .antMatchers("/test/hello") .hasAnyAuthority("perm_hello")//满足该权限 ,只能写单个权限 .antMatchers("/test/hello2") //满足下面权限的一个即可放行 .hasAnyAuthority("perm_hello,perm_hello2") .antMatchers("/test/hello2") //满足下面角色的一个即可放行 .hasRole("admin") .antMatchers("/test/hello3") //满足其中的一个角色即可放行 .hasAnyRole("admin,admin2") .anyRequest() //任何请求 .authenticated()//都需要认证操作 .and() .csrf().disable();//关闭csrf防护 关闭跨域保护; }
依然在配置过滤器的方法中加入 即可 页面
//设置无权限时返回的页面
http.exceptionHandling().accessDeniedPage("/test/unauthen");
@GetMapping("/unauthen")
@ResponseBody
public String unauthen(){
return "无权访问";
}
在配置类中加上如下配置即可
//配置退出
http.logout()
.logoutUrl("/logout") //输入该url表示退出
.logoutSuccessUrl("/test/logout")//成功退出后跳转的页面
.permitAll();
//注入数据源 使用rememberme-me 时需要
@Autowired
private DataSource dataSource;
//配置操作数据库的对象
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
//下面这个api需要导入 orm框架相关的架包 如 mybatisplus
jdbcTokenRepository.setDataSource(dataSource);
//下面这个api可以帮我们自动在数据库创建 rememberme-me 时需要的数据库表一般第一次使用的时候打开
//如果表已经存在会报错
//jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
.and().
rememberMe().
tokenRepository(persistentTokenRepository())//引入操作数据库的类
.tokenValiditySeconds(600)//设置有效时长,单位秒
.userDetailsService(userDetailsService)
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>xx</title>
</head>
<body>
<h1>表单提交</h1>
<form action="/user/login" method="post">
<input type="text" name="username" />
<input type="text" name="password" />
<input type="checkbox"name="remember-me"title="记住密码"/>记住密码<br/>
<input type="submit" />
</form>
</body>
</html>
完整的配置类
package com.li.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.builders.HttpSecurity; 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; import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl; import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; import javax.sql.DataSource; /** 设置用户名密码的第三种方式 通过实现 userDetailsService 接口 * @author liql * @date 2021/9/21 */ @Configuration public class SecurityConfig2 extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } //不注入PasswordEncoder 会报下面的错误 //java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null" @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { /* http.formLogin().loginPage("/login.html") //登录界面 .loginProcessingUrl("/user/login") //用户密码需要提交到的路径 该路径由security管理 不需要我们定义 .defaultSuccessUrl("/test/index")//登录成功后跳转到的页面 .permitAll()// 无条件允许访问 但必须先认证然后不校验权限 .and() .authorizeRequests()//后面写认证配置 .anyRequest() //任何请求 .authenticated()//都需要认证操作 // .antMatchers("/","/test/hello").permitAll() //设置哪些路径可以不用认证就能访问 .and() .csrf().disable();//关闭csrf防护 关闭跨域保护*/ http.formLogin().loginPage("/login.html") //登录界面 .loginProcessingUrl("/user/login") //用户密码需要提交到的路径 该路径由security管理 不需要我们定义 .defaultSuccessUrl("/test/index")//登录成功后跳转到的页面 .permitAll()// 无条件允许访问 但必须先认证然后不校验权限 .and() .authorizeRequests() .antMatchers("/test/hello") .hasAnyAuthority("perm_hello")//满足该权限 ,只能写单个权限 .antMatchers("/test/hello2") //满足下面权限的一个即可放行 .hasAnyAuthority("perm_hello,perm_hello2") .antMatchers("/test/hello2") //满足下面角色即可放行 .hasRole("admin") .antMatchers("/test/hello3") //满足其中的一个角色即可放行 .hasAnyRole("admin,admin2") .anyRequest() //任何请求 .authenticated()//都需要认证操作 //下面是开启记住我功能的配置 .and(). rememberMe(). tokenRepository(persistentTokenRepository())//引入操作数据库的类 .tokenValiditySeconds(600)//设置有效时长,单位秒 .userDetailsService(userDetailsService) .and() .csrf().disable();//关闭csrf防护 关闭跨域保护; //设置无权限时返回的页面 http.exceptionHandling().accessDeniedPage("/test/unauthen"); //配置退出 http.logout() .logoutUrl("/logout") //输入该url表示退出 .logoutSuccessUrl("/test/logout")//成功退出后跳转的页面 .permitAll(); } //注入数据源 使用rememberme-me 时需要,要引入mysql驱动 @Autowired private DataSource dataSource; //配置操作数据库的对象 @Bean public PersistentTokenRepository persistentTokenRepository(){ JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); //下面这个api需要导入 orm框架相关的架包 如 mybatisplus jdbcTokenRepository.setDataSource(dataSource); //下面这个api可以帮我们自动在数据库创建 rememberme-me 时需要的数据库表一般第一次使用的时候打开 //如果表已经存在会报错 //jdbcTokenRepository.setCreateTableOnStartup(true); return jdbcTokenRepository; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。