前言
前面介绍了springboot整合shiro的基本实现案例,这段时间有空又去研究了一下springsecurity,毕竟他是spring全家桶中的一员,我们用springboot开发项目自然springsecurity能够提供更好地集成。
环境搭建
1.新建springboot项目,maven导入springsecurity的包
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.数据库搭建
为了简洁,这里就不提供建表语句了。
项目编码
1.编写自定义登录成功和登录失败处理逻辑(返回json数据,而不是页面跳转),系统默认登录成功会跳回上一个页面,我们需要进行错误提示,所以需要自定义ajax处理。
/** *登录成功处理 */
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Overridepublic void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
Map<String, Object> map = new HashMap<>();map.put("code",200);map.put("msg","登录成功");httpServletRequest.getSession().setAttribute("username",authentication.getName());httpServletResponse.setContentType("application/json;charset=UTF-8");PrintWriter out = httpServletResponse.getWriter();out.write(JSON.toJSONString(map));out.flush();out.close();}
}
/** *登录失败处理 */
@Component
public class MyAuthenticationFailHandler implements AuthenticationFailureHandler {
@Overridepublic void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
Map<String, Object> map = new HashMap<>();map.put("code",300);map.put("msg",e.getMessage()+"请校验用户和密码!");httpServletResponse.setContentType("application/json;charset=UTF-8");PrintWriter out = httpServletResponse.getWriter();out.write(JSON.toJSONString(map));out.flush();out.close();}
}
2.创建加密工具类:由于我们数据库密码是需要加密插入的,这里考虑用md5加密方式,而spring security未提供相关的工具类,这里编写一个MD5工具类:
public class MD5Util {
//加密盐,自己定义private static final String SALT = "com.hui";//加密处理public static String encode(String password) {
password = password + SALT;MessageDigest md5 = null;try {
md5 = MessageDigest.getInstance("MD5");} catch (Exception e) {
throw new RuntimeException(e);}char[] charArray = password.toCharArray();byte[] byteArray = new byte[charArray.length];for (int i = 0; i < charArray.length; i++)byteArray[i] = (byte) charArray[i];byte[] md5Bytes = md5.digest(byteArray);StringBuffer hexValue = new StringBuffer();for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;if (val < 16) {
hexValue.append("0");}hexValue.append(Integer.toHexString(val));}return hexValue.toString();}public static void main(String[] args) {
System.out.println(MD5Util.encode("12345"));}
}
3.我们在后面配置security配置类的时候是需要一个passwordEncoder,否则会出错,security确实封装了一些可用PasswordEncoder 实现类,但是并没有我们要的md5加密方式,所有这里我们需要使用上面的md5工具类进行创建自己的PasswordEncoder。
/*** 自定义密码加密*/
public class CustomPasswordEncoder implements PasswordEncoder {
@Overridepublic String encode(CharSequence charSequence) {
return MD5Util.encode(charSequence.toString());}//比对结果逻辑,可自定义@Overridepublic boolean matches(CharSequence charSequence, String s) {
return s.equals(MD5Util.encode(charSequence.toString()));}
}
4.springsecurity需要提供一个UserDetails 实现类,用于提供核心用户信息。
@Data
public class SysUser implements UserDetails {
private Long id;private String username;private String password;private String sex;private String salt;private String roleIds;private Timestamp createTime;private String createTimeStr;private String telPhone;private String email;private Boolean locked;/*** 权限数据*/private List<SysRole> roles;private List<GrantedAuthority> authorities;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();for (SysRole role : roles) {
GrantedAuthority authority = new SimpleGrantedAuthority(role.getRole());authorities.add(authority);}return authorities;}// 帐户是否被冻结@Overridepublic boolean isAccountNonLocked() {
return !this.locked;}// 帐户是否过期@Overridepublic boolean isAccountNonExpired() {
return true;}// 帐户密码是否过期,一般有的密码要求性高的系统会使用到,每隔一段时间就要求用户重置密码@Overridepublic boolean isCredentialsNonExpired() {
return true;}// 帐号是否可用@Overridepublic boolean isEnabled() {
return true;}
}
5.提供UserDetailsService的实现类
@Service
public class UserService implements org.springframework.security.core.userdetails.UserDetailsService {
@Autowiredprivate SysUserMapper sysUserMapper;@Autowiredprivate SysRoleMapper sysRoleMapper;@Autowiredprivate SysPermissionMapper sysPermissionMapper;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//需自己编写数据库查询权限数据的mapper和service,这里就不提供了SysUser user = sysUserMapper.selectByUserName(s);if (user == null) {
//避免返回null,这里返回一个不含有任何值的User对象,在后期的密码比对过程中一样会验证失败throw new UsernameNotFoundException("登录用户不存在!");}//查询用户的角色信息,并返回存入user中List<SysRole> roles = sysRoleMapper.selectByUserName(user.getUsername());List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();for (SysRole role : roles) {
//需自己编写数据库查询权限数据的mapper和service,这里就不提供了List<SysPermission> perms = sysPermissionMapper.findPermissionsByRoleId(role.getId());for (SysPermission perm : perms) {
GrantedAuthority authority = new SimpleGrantedAuthority(perm.getPermission());authorities.add(authority);}}//设置用户拥有的权限信息user.setAuthorities(authorities);//设置用户拥有的角色信息user.setRoles(roles);return user;}
}
7.springboot整合security配置类
@Configuration
@EnableWebSecurity
public class SercueityConfig extends WebSecurityConfigurerAdapter {
@Autowiredprivate MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;@Autowiredprivate MyAuthenticationFailHandler myAuthenticationFailHandler;@Autowiredprivate UserService userService;@Beanpublic CustomPasswordEncoder getPasswordEncode() {
return new CustomPasswordEncoder();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(getPasswordEncode());}@Overrideprotected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable() //当系统用到了frame嵌入页面,需要配置此项,否则会出错.and().csrf().disable()//关闭csrf,解决跨域报错问题.authorizeRequests()//静态资源放行.antMatchers("/js/**", "/css/**", "/fonts/**", "/images/**", "/lib/**").permitAll()//登录页面和检验登录页面进行放行.antMatchers("/checkLogin", "/login").permitAll()//其余请求都要进行授权认证.anyRequest().authenticated().and().formLogin()//自定义登录页面,springboot自带登录页面.loginPage("/login")//自定义登录处理,可以不在controller中配置路由,这就是一个虚设其实.loginProcessingUrl("/checkLogin")//自定义登录成功处理类.successHandler(myAuthenticationSuccessHandler)//自定义登录失败处理类.failureHandler(myAuthenticationFailHandler);}
}
前端
1.登录页面主要逻辑
<div class="login layui-anim layui-anim-up"><div class="message">管理登录</div><div id="darkbannerwrap"></div><form method="post" class="layui-form" id="form"><input name="username" placeholder="用户名" type="text" lay-verify="required" class="layui-input"><hr class="hr15"><input name="password" lay-verify="required" placeholder="密码" type="password" class="layui-input"><hr class="hr15"><input value="登录" lay-submit lay-filter="login" style="width:100%;" type="button"><hr class="hr20"></form>
</div>
<script th:inline="javascript">$(function () {
layui.use(['layer', 'form'], function () {
let form = layui.form, layer = layui.layer;form.on('submit(login)', function (data) {
$.post("/checkLogin", $("#form").serialize(), function (res) {
if (res.code == 200) {
//校验成功跳到主页window.location.href = "/index1";} else {
//弹出错误信息layer.msg(res.msg);return false;}});});});});
</script>
测试
登录失败
登陆成功