在用会了shiro后,最近学习使用spring security进行用户登录校验。
项目说明:
1、项目前后端分离架构。
2、后续想加上oauth2进行单点登录或用户中心。
(一)、spring security 主要用到的类和接口介绍
(二)、pom.xml加入依赖包
org.springframework.boot
spring-boot-starter-security
(三)、 几个重要类和接口的实现
package com.qwer.wen.common.auth;
import com.qwer.wen.common.auth.user.MD5AndAaltPasswordEncoder;
import com.qwer.wen.system.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @ClassName SpringSecurityConfig
* @Description:
* @Author ZHOUWEN
* @Date 2020/4/22 22:05
**/
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("userDetailsServiceImpl")
private UserDetailsService userDetailsService;
@Autowired
@Qualifier("UserAuthenticationSuccessHandler")
private UserAuthenticationSuccessHandler userAuthenticationSuccessHandler;
@Autowired
@Qualifier("UserAuthenticationFailureHandler")
private UserAuthenticationFailureHandler userAuthenticationFailureHandler;
/*
* 对每个请求进行详细的安全性控制在于重载configure(HttpSecurity)方法,它为不同的url路径都有选择地应用安全性
* */
@Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("进入spring security.........");
http.authorizeRequests()
.anyRequest().authenticated().and()
.formLogin()
.successHandler(userAuthenticationSuccessHandler)
.failureHandler(userAuthenticationFailureHandler)
.loginProcessingUrl("/login").permitAll().and()
//.httpBasic().and().successHandler(null).failureHandler(null).and()
.logout().permitAll().and()
//同一账号同时登录最大用户数maximumSessions(1)
.sessionManagement().maximumSessions(1);
}
//从数据库中读取用户数据出来,指定密码的加密方式
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
System.out.println("验证用户......");
auth.userDetailsService(userDetailsService).passwordEncoder(new MD5AndAaltPasswordEncoder());
}
}
package com.qwer.wen.system.service.impl;
import com.qwer.wen.common.auth.user.UserDetailsDto;
import com.qwer.wen.common.spring.SpringUtil;
import com.qwer.wen.common.utils.bean.PojoUtils;
import com.qwer.wen.system.service.SystemUserService;
import com.qwer.wen.user.dao.pojo.TbUser;
import com.qwer.wen.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
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.Component;
import org.springframework.stereotype.Service;
/**
* @ClassName UserDetailsServiceImpl
* @Description:
* @Author ZHOUWEN
* @Date 2020/4/25 16:27
**/
@Service("userDetailsServiceImpl")
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserService userService;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
System.out.println("进入loadUserByUsername:"+s);
TbUser userByLoginUser = userService.getUserByLoginUser(s);
UserDetailsDto userDetails = new UserDetailsDto();
//将userByLoginUser 赋值给 userDetails
userDetails.setLoginPassword(userByLoginUser.getLoginPassword());
userDetails.setLoginUser(userByLoginUser.getLoginUser());
return userDetails;
}
}
package com.qwer.wen.common.auth.user;
import io.netty.buffer.Unpooled;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
/**
* @ClassName UserDetailsDto
* @Description:
* @Author ZHOUWEN
* @Date 2020/4/25 16:32
**/
public class UserDetailsDto implements UserDetails {
private String loginUser;
private String loginPassword;
Collection extends GrantedAuthority> grantedAuthorities;
boolean isAccountNonExpired = true;
boolean isAccountNonLocked = true;
boolean isCredentialsNonExpired = true;
boolean isEnabled = true;
public UserDetailsDto(){
}
public UserDetailsDto(String loginUser,String loginPassword,Collection extends GrantedAuthority> grantedAuthorities
,boolean isAccountNonExpired,boolean isAccountNonLocked,boolean isCredentialsNonExpired ,boolean isEnabled){
//用户名和密码
this.loginUser = loginUser;
this.loginPassword= loginPassword;
//各种标记
this.isAccountNonExpired=isAccountNonExpired;
this.isAccountNonLocked=isAccountNonLocked;
this.isCredentialsNonExpired = isCredentialsNonExpired;
this.isEnabled = isEnabled;
//权限
this.grantedAuthorities =grantedAuthorities;
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return loginPassword;
}
@Override
public String getUsername() {
return loginUser;
}
@Override
public boolean isAccountNonExpired() {
return isAccountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return isAccountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return isCredentialsNonExpired;
}
@Override
public boolean isEnabled() {
return isEnabled;
}
public String getLoginUser() {
return loginUser;
}
public void setLoginUser(String loginUser) {
this.loginUser = loginUser;
}
public String getLoginPassword() {
return loginPassword;
}
public void setLoginPassword(String loginPassword) {
this.loginPassword = loginPassword;
}
}
package com.qwer.wen.common.auth.user;
import org.apache.commons.codec.binary.Hex;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
/**
* @ClassName MD5AndAaltPasswordEncoder
* @Description:
* @Author ZHOUWEN
* @Date 2020/4/27 23:05
**/
public class MD5AndAaltPasswordEncoder implements PasswordEncoder {
//加密
@Override
public String encode(CharSequence charSequence) {
return MD5andSAL(charSequence.toString());
}
//比对
@Override
public boolean matches(CharSequence charSequence, String encoderPassword) {
return verify(charSequence.toString(),encoderPassword);
}
/**
* 普通MD5
*/
public static String MD5(String input) {
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
return "check jdk";
} catch (Exception e) {
e.printStackTrace();
return "";
}
char[] charArray = input.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();
}
/**
* 加盐MD5
* @author daniel
* @time 2016-6-11 下午8:45:04
* @param password
* @return
*/
public static String MD5andSAL(String password) {
Random r = new Random();
StringBuilder sb = new StringBuilder(16);
sb.append(r.nextInt(99999999)).append(r.nextInt(99999999));
int len = sb.length();
if (len < 16) {
for (int i = 0; i < 16 - len; i++) {
sb.append("0");
}
}
String salt = sb.toString();
password = MD5(password + salt);
char[] cs = new char[48];
for (int i = 0; i < 48; i += 3) {
cs[i] = password.charAt(i / 3 * 2);
char c = salt.charAt(i / 3);
cs[i + 1] = c;
cs[i + 2] = password.charAt(i / 3 * 2 + 1);
}
return new String(cs);
}
/**
* 校验加盐后是否和原文一致
* @author daniel
* @time 2016-6-11 下午8:45:39
* @param password
* @param DBMD5
* @return
*/
public static boolean verify(String password, String DBMD5) {
char[] cs1 = new char[32];
char[] cs2 = new char[16];
for (int i = 0; i < 48; i += 3) {
cs1[i / 3 * 2] = DBMD5.charAt(i);
cs1[i / 3 * 2 + 1] = DBMD5.charAt(i + 2);
cs2[i / 3] = DBMD5.charAt(i + 1);
}
String salt = new String(cs2);
return md5Hex(password + salt).equals(new String(cs1));
}
/**
* 获取十六进制字符串形式的MD5摘要
*/
private static String md5Hex(String src) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] bs = md5.digest(src.getBytes());
return new String(new Hex().encode(bs));
} catch (Exception e) {
return null;
}
}
}
package com.qwer.wen.common.auth;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName UserAuthenticationFailureHandler
* @Description:
* @Author ZHOUWEN
* @Date 2020/4/28 22:45
**/
@Component("UserAuthenticationFailureHandler")
public class UserAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
System.out.println( httpServletRequest.getRequestURI());
System.out.println("登录失败");
httpServletResponse.setContentType("text/html;charset=UTF-8");
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.getWriter().write("我试试登录失败。。。。。处理器");
}
}
package com.qwer.wen.common.auth;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName UserAuthenticationSuccessHandler
* @Description: 登录成功
* @Author ZHOUWEN
* @Date 2020/4/25 10:11
**/
@Component("UserAuthenticationSuccessHandler")
public class UserAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
System.out.println( httpServletRequest.getRequestURI());
System.out.println("登录成功");
httpServletResponse.setContentType("text/html;charset=UTF-8");
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.getWriter().write("我试试登录成功。。。。。处理器");
}
}
(四)登录后
前后端分离后我们返回登录后的地址即可。我这还没实现前后端分离。