注意,在配置过滤器时 每个权限/角色配置操作前面都要写上请求路径 如: .anyRequest() 请求路径 .authenticated()//都需要认证操作 是权限/角色操作
三种用户认证方式(设置用户名密码的)
第一种:通过配置文件(非查库)
应用添加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;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String password = passwordEncoder.encode("123456");
auth.inMemoryAuthentication().withUser("lql").password(password).roles("");
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
第三种 通过实现 UserDetailsService接口设置用户名密码(可查库)
分别定义一个配置类和接口实例类即可, 用户名密码查库操作可定义在该实例中只需要替换返回对象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;
@Configuration
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@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;
@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;
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
private static ConcurrentHashMap<String,String> mydb=new ConcurrentHashMap();
static {
mydb.put("lql", "111");
mydb.put("xiaohong", "123");
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList("roles");
String password=mydb.get(username);
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")
.defaultSuccessUrl("/test/index")
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.csrf().disable();
}
添加依赖和配置 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">
<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("perm_hello1,perm_hello2");
String password=mydb.get(username);
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")
.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();
}
基于角色访问
给用户设置角色
与设置权限的地方相同 ,在实例类中 设置权限的地方增加 ROLE_xxx 即可 加“ROLE_”前缀的原因是,在配置过滤器时 如写个角色为 “admin” 但是在底层会校验“ROLE_admin”;
List<GrantedAuthority> authorityList =
AuthorityUtils
.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")
.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();
}
自定义无权限时的返回页面
依然在配置过滤器的方法中加入 即可 页面
http.exceptionHandling().accessDeniedPage("/test/unauthen");
@GetMapping("/unauthen")
@ResponseBody
public String unauthen(){
return "无权访问";
}
退出登录
在配置类中加上如下配置即可
http.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/test/logout")
.permitAll();
基于注解配置
自动登录
|