一 安全性框架
- apache shiro 比较简单易用,不依赖于spring,应用场景:传统SSM项目。
- springsecurity 比较复杂,功能较强大,属于spring框架技术,应用场景:springboot+springcloud
springsecurity框架内容:
- springsecurity基础入门
- jwt+sprinsecurity 组合,多用于微服务分布式开发中
- jwt+springsecurity+springcloud
- jwt+springsecurity+springcloud+前端(angular.js vue.js)
二 了解springsecurity的核心组件
- SecurityContext springsecurity的上下文,保存重要对象的信息,比如,用户信息
- SecurityContextHolder 通过该工具获取SecurityContext
- Authencation "认证"的意思,理解成认证的主体,获取认证主体的信息,账号和密码
- UserDetails 接口 表示“用户的详情信息”,规范了用户的详情信息(或者规范了pojo的定义,对用户对象的约束,用户的pojo类要实现该接口)
- UserDetailsService 接口, 仅有一个方法:loadUserByUsername(String username),对用户验证接口的约束。
三 Springsecurity入门程序
- 扩展用户的pojo类(UserDetails接口)
public class SysUser implements UserDetails{
private String id;
private String usercode;
private String username;
private String password;
private String salt;
private String locked;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id == null ? null : id.trim();
}
public String getUsercode() {
return usercode;
}
public void setUsercode(String usercode) {
this.usercode = usercode == null ? null : usercode.trim();
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username == null ? null : username.trim();
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt == null ? null : salt.trim();
}
public String getLocked() {
return locked;
}
public void setLocked(String locked) {
this.locked = locked == null ? null : locked.trim();
}
@Override //返回用户的权限信息
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override //判断账号是否过期
public boolean isAccountNonExpired() {
return true;
}
@Override //判断 账号是否被锁定
public boolean isAccountNonLocked() {
return this.locked.equals("0");
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
- 实现业务类(UserDetailsService接口)
@Service
public class SysUserService implements UserDetailsService {
@Autowired
private SysUserMapper userMapper;
//根据账号查找用户的方法;账号名是唯一约束
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("验证(Service): " + username);
SysUserExample example = new SysUserExample();
SysUserExample.Criteria criteria = example.createCriteria();
criteria.andUsercodeEqualTo(username);
List<SysUser> list = userMapper.selectByExample(example);
if(list!=null && list.size()==0) { //找不到用户,抛异常
throw new UsernameNotFoundException("账号不存在");
}
return list.get(0);
}
}
- springsecurity配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SysUserService userService;
//指定加密算法 springsecurity必须要用加密,推荐BCrypt算法,64位的长度
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); //盐放在密文中
}
//指定用户的验证业务对象
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
//框架的安全性配置(重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置是一种链式风格
http.authorizeRequests()
.anyRequest()
.authenticated() //所有请求都要被验证
.and()
.formLogin()
.usernameParameter("username")
.passwordParameter("password") //框架会提供默认的登录表单页
.successHandler(new AuthenticationSuccessHandler() {//登录成功后的回调接口
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
//返回json数据
PrintWriter out = response.getWriter();
//获取用户的身份信息(类型:UserDetails)
SysUser user = (SysUser) authentication.getPrincipal();
RespBean respBean = RespBean.ok("登录成功",user);
//把respBean转成json对象,返回到前端
String json = new ObjectMapper().writeValueAsString(respBean);
System.out.println("成功返回json对象:" + json);
out.write(json);
out.flush();
out.close();
}
})
.failureHandler(new AuthenticationFailureHandler() { //登录失败后的回调接口
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
//返回json数据
PrintWriter out = response.getWriter();
//登录失败,主要是处理异常
RespBean respBean = RespBean.error("登录失败");
if(e instanceof LockedException) {
respBean.setMsg("账号被锁定");
} else if(e instanceof CredentialsExpiredException) {
respBean.setMsg("密码过期");
} else if(e instanceof DisabledException) {
respBean.setMsg("账号被禁用");
} else if(e instanceof BadCredentialsException) {
respBean.setMsg("账号或密码错误");
}
String json = new ObjectMapper().writeValueAsString(respBean);
System.out.println("登录失败返回的json: " + json);
out.write(json);
out.flush();
out.close();
}
})
.permitAll()
.and()
.logout()
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
}
})
.permitAll()
.and()
.csrf().disable(); //禁用跨域攻击
}
}