1.依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.webSecurityConfigurerAdapter
通过重写抽象接口webSecurityConfigurerAdapter,在加上注解@EnableWebSecurity,即可实现web 的安全配置。
webSecurityConfigurerAdapter Config模块一共有三个builder(构造程序)。
(1)AuthenticationManagerBuilder:认证相关builder,用来配置全局的认证相关的信息。它包含AuthenticationProvider和UserDetailsService,前者是认证服务提供者,后者是用户详情查询服务。
(2)HttpSecurity:进行权限控制规则相关配置。
(3)WebSecurity:进行全局请求忽略规则配置、HTTPFirewall配置、debug配置、全局securityFilterChain配置。
简单的config配置如下:
package com.example.demo.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.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;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//进行密码的加密
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
//configure(AuthenticationManagerBuilder auth),
// 这个方法的主要作用就是设置用户的权限,从数据库获取用户信息之类的
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}
//通过http对象的authorizeRequests()方法定义url访问权限。
// 这个方法的主要作用就是设置某个请求需要什么权限才能访问
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()//定义哪些url需要被保护,哪些不需要被保护。
.antMatchers("/login/**")
.hasRole("user")//定义/login/下的所有url,只有拥有user角色的用户才有访问权限
.antMatchers("/admin/**")
.hasAuthority("admin")//定义/admin/下的所有url,只有拥有admin权限的用户才有访问权限
.anyRequest()
.authenticated()//除上面外的所有请求全部需要鉴权认证
.and()
.formLogin()//自定义用户登录页面
.loginProcessingUrl("/login")//登录表单详细配置
.loginPage("/Login")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(new AuthenticationSuccessHandler() {//登录成功的配置
@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication auth) throws IOException, ServletException {
Object principal = auth.getPrincipal();//获取用户
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
resp.setStatus(200);
Map<String, Object> map = new HashMap<>();
map.put("status", 200);
map.put("msg", principal);
ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close();
}
})//登录失败操作
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
resp.setStatus(401);
Map<String, Object> map = new HashMap<>();
map.put("status", 401);
if (e instanceof LockedException) {
map.put("msg", "账户被锁定,登录失败!!");
} else if (e instanceof BadCredentialsException) {
map.put("msg", "账户名或密码输入错误,登录失败!!");
} else if (e instanceof DisabledException) {
map.put("msg", "账户被禁用,登录失败!!");
} else if (e instanceof AccountExpiredException) {
map.put("msg", "账户已过期,登录失败!!");
} else if (e instanceof CredentialsExpiredException) {
map.put("msg", "密码已过期,登录失败!!");
} else {
map.put("msg", "登录失败");
}
ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close();
}
})
.permitAll()
.and()
//logout操作,注销登录操作
.logout()
.logoutUrl("/logout")
.clearAuthentication(true)//清除身份认证信息,默认为true,表示清除
.invalidateHttpSession(true)//是否使session失效,默认为true
.addLogoutHandler(new LogoutHandler() {
@Override
public void logout(HttpServletRequest res, HttpServletResponse resp, Authentication auth) {
}
})
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.sendRedirect("/login_page");
}
})
.and()
.cors()
.and()
.csrf()
.disable();
}
}
配置用户加密
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}
//用户信息是存储在数据库中的,因此需要在用户注册时对密码进行加密处理。
//用户将密码从前端传来之后,通过调用BCryptPasswordEncoder实例中的encode方法对面进行加密处理,加密完成后将密文存入数据库
配置用户实体类
package com.example.demo.bean;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Data
@Accessors(chain = true)
@TableName("user")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
//使用SpringSecurity从数据库里拿用户信息需要实现这个接口
public class User implements UserDetails {
@TableId
private Integer id;
private String username;
private String password;
// 用transient关键字标记的成员变量不参与序列化过程。
private String role;//用户角色
private boolean enabled;//是否禁用
private boolean locked;//是否锁死
private transient Collection<? extends GrantedAuthority> authorities;
public User(String username,String password,Collection<? extends GrantedAuthority> authorities){
this.username = username;
this.password = password;
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
String str[] = this.role.split(",");
for(String s: str){
authorities.add(new SimpleGrantedAuthority(s));
}
return authorities;
}
/**
* 用户账号是否过期
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 用户账号是否被锁定
*/
@Override
public boolean isAccountNonLocked() {
return !locked;
}
/**
* 用户密码是否过期
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
}
hasRole | 当前用户是否拥有指定角色 |
---|---|
hasAnyRole | 多个角色以逗号进行分割的字符串。如果当前用户拥有指定角色中的任意一个,则返回true |
hasAuthority | 等同与hasRole ,但是指的是权限 |
– | – |
hasAnyAuthority | 等同于hasAnyRole |
Principle | 代表当前用户的principle对象 |
– | – |
premitAll | 表示允许所有的 |
denyAll | 表示拒绝所有的 |
– | – |
isAnonymous | 当前用户是否是一个匿名用户 |
isRememberme | 表示当前用户是否是通过Remember-Me自动登录的 |
– | – |
isAuthenticated | 表示当前用户是否已经登录认证成功了 |
authenticated():保护url,需要用户登录,如:anyRequest().anthenticated()代表其他未配置的页面都已经授权。
hasIpAddress(String …):用于限制ip地址或子网。
创建UserService
@Service
@Transactional
public class UserServiceImpl implements MyUserService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = userMapper.selectOne(new QueryWrapper<User>().eq("username",s));
System.out.println("loaduserByName 方法");
if(user==null)
throw new UsernameNotFoundException("该用户名不存在");
//查询权限信息
List<SimpleGrantedAuthority> authorities = createAuthorities(user.getRole());
System.out.println("user 的 Authorities权限是:"+user.getAuthorities());
return user.setAuthorities(authorities);
}
private List<SimpleGrantedAuthority> createAuthorities(String roleStr){
String[] roles = roleStr.split(",");
List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
for (String role : roles) {
simpleGrantedAuthorities.add(new SimpleGrantedAuthority(role));
}
return simpleGrantedAuthorities;
}
}
loadUserByUsername方法将在用户登录时自动调用(这是个坑点)