springsecurity主要是 认证 (密码登录) 与 授权 (角色权限管理)
下面一个简单项目例子,使用springsecurity的权限管理
- MyUserDetailsService#loadUserByUsername 用户登录查询
- SecurityConfig#configure 配置springsecurity行为
- Controller中的@PreAuthorize就是判断用户角色权限
理解权限关系怎么在springsecurity中实现
- 用户:abc(角色admin,userRole),ccc(角色userRole)
- 角色:admin (权限Lsit,Add),userRole(权限List)
- 资源(权限):List,Add
项目结构
http://127.0.0.1:8080/index 首页,不用权限
http://127.0.0.1:8080/list 会跳转到login,输入ccc,456 登录 (springsecurity自动拦截login的post请求,进入MyUserDetailsService#loadUserByUsername),可以看http://127.0.0.1:8080/list,但是http://127.0.0.1:8080/add需要admin角色,会报错
http://127.0.0.1:8080/logout 跳转到index,虽然没有在controller写logout,但是springsecurity自动有了( 在SecurityConfig#configure配置的loguot端点)
http://127.0.0.1:8080/login 输入abc,123登录,可以看 /list与 /add
基本完成权限控制与角色权限控制
密码没有BCrypt加密(字符串开头$2a),会报错 Encoded password does not look like BCrypt
//App.java
package boottest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author zhanghui
* @date 2019/4/24
*/
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class);
System.out.printf("test here!");
}
}
//SysUser.java
package boottest.auth;
/**
* @author zhanghui
* @date 2019/4/24
*/
public class SysUser {
private String userName;
private String password;
private String roles;
public SysUser(String userName, String password, String roles) {
this.userName = userName;
this.password = password;
this.roles = roles;
}
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 getRoles() {
return roles;
}
public void setRoles(String roles) {
this.roles = roles;
}
}
//SecurityConfig.java
package boottest.auth;
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.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author zhanghui
* @date 2019/4/24
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 启用方法级别的权限认证
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsService myUserDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
// 允许所有用户访问"/"和"/index.html"
http.authorizeRequests()
.antMatchers("/", "/index").permitAll()
.anyRequest().authenticated() // 其他地址的访问均需验证权限
.and()
.csrf().disable() //暂时关闭csrf,不知道java获取csrf放入form表单
.formLogin() // form提交登录
.loginPage("/login") // 登录页
.failureUrl("/login-error").permitAll()
.and()
.logout() // 添加 /logout 访问点,能退出
.logoutSuccessUrl("/index"); //退出后访问
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
//MyUserDetailsService.java
package boottest.auth;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.stereotype.Service;
import java.util.List;
/**
* @author zhanghui
* @date 2019/4/24
*/
@Service
public class MyUserDetailsService implements UserDetailsService {
/**
* 根据用户的角色判断权限
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//模拟数据库
SysUser sysUser;
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
if("abc".equals(username)){
sysUser=new SysUser("abc",passwordEncoder.encode("123")),"ROLE_admin,ROLE_user");
}else if("ccc".equals(username)){
sysUser=new SysUser("ccc",passwordEncoder.encode("456")), "ROLE_user");
}else {
sysUser=null;
}
if (null == sysUser) {
throw new UsernameNotFoundException(username);
}
// authorities 是 roles 集合
List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(sysUser.getRoles());
//上面是根据用户名查出用户信息,下面才会比较传来的password是否正确
return new User(sysUser.getUserName(), sysUser.getPassword(), authorities);
}
}
//Controller.java
package boottest.auth;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @author zhanghui
* @date 2019/4/24
*/
@RestController
@ResponseBody
public class Controller {
@GetMapping(value = {"/",""})
public String defaut(){
return "defaut";
}
@GetMapping("/index")
public String index(){
return "index";
}
// direct request to "/logout" for logout
@RequestMapping("/login")
public String login(){
return "<form action=\"/login\" method=\"post\">\n" +
" <label for=\"username\">Username</label>:\n" +
" <input type=\"text\" id=\"username\" name=\"username\" autofocus=\"autofocus\" /> <br />\n" +
" <label for=\"password\">Password</label>:\n" +
" <input type=\"password\" id=\"password\" name=\"password\" /> <br />\n" +
" <input type=\"submit\" value=\"Login\" />\n" +
"</form>";
}
@GetMapping("/login-error")
public String loginerror(){
return "login-error";
}
// @PreAuthorize("hasAuthority('ROLE_user')") //判断角色
@PreAuthorize("hasRole('user')") //同上,判断角色,会自动加 前缀 ROLE_
@GetMapping("/list")
public String list() {
//程序内判断role, 或者@AuthenticationPrincipal注解取得MyUserDetailsService内的UserDetails
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_user"))){
System.out.println("user role2");
}
return "to list";
}
@PreAuthorize("hasAuthority('ROLE_admin')")
@GetMapping("/add")
public String add() {
return "to add";
}
}