本章节基础入门篇
话不多说开干
- 首先引入springsecurity的pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
- 首先你要创建
SecurityConfig(名可随便取)
继承WebSecurityConfigurerAdapter
类,重写三个configure
方法
WebSecurityConfigurerAdapter
是适配器类,在配置的时候需要我们自己写配置类去继承他,然后编写自己所特殊需要的配置
import com.helei.security.MyAccessDeniedHandler;
import com.helei.security.MyAuthenticationEntryPoint;
import com.helei.security.MyAuthenticationFailureHandler;
import com.helei.security.MyAuthenticationSuccessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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 javax.annotation.Resource;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class SecurityJwtConfig extends WebSecurityConfigurerAdapter {
//成功返回
@Resource
MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
//失败返回控制器
@Resource
MyAuthenticationFailureHandler myAuthenticationFailureHandler;
//未登录时
@Resource
MyAuthenticationEntryPoint myAuthenticationEntryPoint;
//权限不足时
@Resource
MyAccessDeniedHandler myAccessDeniedHandler;
//密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Resource
private PasswordEncoder passwordEncoder;
@Resource
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/test/a").permitAll()
.anyRequest().authenticated()
//授权
.and()
.formLogin()
.loginProcessingUrl("/login")
.usernameParameter("name")
.passwordParameter("pass")
//登录成功
.successHandler(myAuthenticationSuccessHandler)
//登录失败
.failureHandler(myAuthenticationFailureHandler)
.and()
.exceptionHandling()
.accessDeniedHandler(myAccessDeniedHandler)
.authenticationEntryPoint(myAuthenticationEntryPoint);
http.csrf().disable();
}
/**
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
}
- 首先看
PasswordEncoder
如果不需要加密可用NoOpPasswordEncoder()
,但是一般数据库存的都是加密密码所以一般用BCryptPasswordEncoder()
//密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/test/a").permitAll()
.anyRequest().authenticated()
//授权
.and()
.formLogin()
.loginProcessingUrl("/login")
.usernameParameter("name")
.passwordParameter("pass")
//登录成功
.successHandler(myAuthenticationSuccessHandler)
//登录失败
.failureHandler(myAuthenticationFailureHandler)
.and()
.exceptionHandling()
.accessDeniedHandler(myAccessDeniedHandler)
.authenticationEntryPoint(myAuthenticationEntryPoint);
http.csrf().disable();
}
- 我们来看看这个
configure
,它可以配置一下放行url,权限,异常等
antMatchers(" ").permitAll().anyRequest().authenticated()
配置的路径可放行,其他需要验证
.formLogin()
可配置登录路径
.usernameParameter()
配置登录账号参数
.passwordParameter()
配置登录密码参数
上面的配置说明登录路径为http://localhost/login?name=&pass=
如果不配置usernameParameter()
和passwordParameter()
,默认是username
和password
关键地方来了
,如果你是前后一起的那么用 成功.successForwardUrl(“”)
跳转登录成功后路径,失败同理.failureForwardUrl()
,一般都是前后分离,所以不这里不细讲
前后分离项目我们登录成功或者失败都需要返回json
给前端所以我们需要自定义返回的值
登录成功是由AuthenticationSuccessHandler进行处理结果的
//处理登录成功返回
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter out = response.getWriter();
out.write(new ObjectMapper().writeValueAsString(Resp.getNew().success("登录成功")));
out.flush();
out.close();
}
}
失败处理控制器
//失败处理控制器
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter out = response.getWriter();
if (exception instanceof AccountExpiredException) {
//账号过期
out.write(new ObjectMapper().writeValueAsString(Resp.getNew().fail("账号过期")));
} else if (exception instanceof BadCredentialsException) {
//密码错误
out.write(new ObjectMapper().writeValueAsString(Resp.getNew().fail("密码错误")));
} else if (exception instanceof CredentialsExpiredException) {
//密码过期
out.write(new ObjectMapper().writeValueAsString(Resp.getNew().fail("密码过期")));
} else if (exception instanceof DisabledException) {
//账户不可用
out.write(new ObjectMapper().writeValueAsString(Resp.getNew().fail("账户不可用")));
} else if (exception instanceof LockedException) {
//账户锁定
out.write(new ObjectMapper().writeValueAsString(Resp.getNew().fail("账户锁定")));
} else if (exception instanceof InternalAuthenticationServiceException) {
//用户不存在
out.write(new ObjectMapper().writeValueAsString(Resp.getNew().fail("用户不存在")));
} else {
out.write(new ObjectMapper().writeValueAsString(Resp.getNew().fail("其他错误")));
}
out.flush();
out.close();
}
}
没有登录访问
/**
* 未登录访问无权限资源时异常
*/
@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter out = response.getWriter();
out.write(new ObjectMapper().writeValueAsString(Resp.getNew().success("未登录")));
out.flush();
out.close();
}
}
权限不足
/**
* 权限不足异常
*/
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter out = response.getWriter();
out.write(new ObjectMapper().writeValueAsString(Resp.getNew().success("权限不足")));
out.flush();
out.close();
}
}
我们需要从数据库读取账户密码,所以实现UserDetailsService,这里role现在定死了,有需要可从数据库查询
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
@Resource
SecurityUserMapper securityUserMapper;
//根据 账号查询用户信息
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//将来连接数据库根据账号查询用户信息
//暂时采用模拟数据
// UserDetails build = User.withUsername("admin").password("123").authorities("a").build();
//数据库查询
SecurityUserEntity securityUserEntity = securityUserMapper.selectOne(new QueryWrapper<SecurityUserEntity>()
.eq("name", username));
String role = "update,a";
String[] roles = role.split(",");
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (String s : roles) {
authorities.add(new SimpleGrantedAuthority("ROLE_"+s));
}
// UserDetails build = User.withUsername(username).password(securityUserEntity.getPassword()).roles("a").build();
return new User(username, securityUserEntity.getPassword(), authorities);
}
}
如要用到注解权限需加上
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
securedEnabled 开启 @Secured
prePostEnabled 开启@PreAuthorize
实例
@RestController
@RequestMapping("test")
public class AppUserController {
@GetMapping("/a")
public String index(){
return "security jwt";
}
@PostMapping("/role")
@PreAuthorize("hasAnyAuthority('ROLE_a')")
public String roleInfo(){
return "需要获得ROLE_a权限,才可以访问";
}
// @PreAuthorize("hasAnyAuthority('kdream')")
@PostMapping("/roles")
@Secured("ROLE_a")
public String rolekdream(){
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
System.out.println(new Gson().toJson(userDetails));
return "需要获得kdream权限,才可以访问";
}
}
下章中级篇https://blog.youkuaiyun.com/he_lei/article/details/115599592