spring boot+spring security+jwt+vue整合前后端分离token权限认证项目详细过程代码 含AES加密
程序员文章站
2022-06-13 15:17:52
...
准备前提条件:已搭好一个spring boot后台项目及vue前台项目
目录
2、继承WebSecurityConfigurerAdapter适配器
3、自定义用户名密码校验 MyAuthenticationProvider
4、登录成功处理逻辑 CustomizeAuthenticationSuccessHandler
5、登录失败处理逻辑 CustomizeAuthenticationFailureHandler
6、匿名用户访问无权限资源时的异常处理 CustomizeAuthenticationEntryPoint
7、会话失效(账号被挤下线)处理逻辑 CustomizeSessionInformationExpiredStrategy
8、登出成功处理逻辑 CustomizeLogoutSuccessHandler
9、UserDetailsService 的实现 UserDetailsServiceImpl
10、访问权限过滤器 AuthorizationFilter
一、后台
1、依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.60</version>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2、继承WebSecurityConfigurerAdapter适配器
import com.example.office.filter.AuthorizationFilter;
import org.springframework.beans.factory.annotation.Autowired;
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.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
// 自定义用户名密码校验
@Autowired
MyAuthenticationProvider myAuthenticationProvider;
//登录成功处理逻辑
@Autowired
CustomizeAuthenticationSuccessHandler authenticationSuccessHandler;
//登录失败处理逻辑
@Autowired
CustomizeAuthenticationFailureHandler authenticationFailureHandler;
//匿名用户访问无权限资源时的异常
@Autowired
CustomizeAuthenticationEntryPoint authenticationEntryPoint;
//会话失效(账号被挤下线)处理逻辑
@Autowired
CustomizeSessionInformationExpiredStrategy sessionInformationExpiredStrategy;
//登出成功处理逻辑
@Autowired
CustomizeLogoutSuccessHandler logoutSuccessHandler;
@Bean
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public PersistentTokenRepository getPersistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(myAuthenticationProvider);
// auth.userDetailsService(userDetailsService());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests().
// 所以请求都需要认证
anyRequest().authenticated().
// 登出
and().logout().logoutUrl("/logout").
permitAll(). // 允许所有用户
logoutSuccessHandler(logoutSuccessHandler). // 登出成功处理逻辑
// deleteCookies("JESSIONID"). // 登出之后删除cookie
// 登入
and().formLogin().loginProcessingUrl("/login").usernameParameter("username").passwordParameter("password").permitAll().
successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler).
// 记住我
and().rememberMe().tokenRepository(getPersistentTokenRepository()).
tokenValiditySeconds(15*60*1000).
// 异常处理(权限拒绝、登录失效等)
and().exceptionHandling().
authenticationEntryPoint(authenticationEntryPoint). // 匿名用户访问无权限资源时的异常处理
// 访问过滤器
and().addFilter(new AuthorizationFilter(authenticationManager())).httpBasic().
// 会话管理
and().sessionManagement().
maximumSessions(1). // 同一账号同时登录最大用户数
expiredSessionStrategy(sessionInformationExpiredStrategy); // 会话失效(账号被挤下线)处理逻辑
// 禁用缓存
http.headers().cacheControl();
}
}
3、自定义用户名密码校验 MyAuthenticationProvider
import com.example.office.utils.AesEncryptUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
/**
* 自定义用户名密码校验
*/
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 获取用户输入的用户名和密码
String username = authentication.getName();
String password = authentication.getCredentials().toString();
// 登录密码前端加密传输,需先解密
password = AesEncryptUtil.decrypt(password);
// 获取封装用户的信息
UserDetails user = userDetailsService.loadUserByUsername(username);
if (user == null) {
throw new BadCredentialsException("查无此用户");
}
// 进行密码的对比
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
boolean flag = bCryptPasswordEncoder.matches(password, user.getPassword());
// 校验通过
if (flag) {
// 将权限信息也封装进去
return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
} else {
throw new BadCredentialsException("用户名或密码错误!");
}
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
4、登录成功处理逻辑 CustomizeAuthenticationSuccessHandler
import com.example.office.utils.JsonResult;
import com.example.office.utils.TokenUtil;
import net.minidev.json.JSONValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
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;
/**
* 登录成功请求处理
*/
@Component
public class CustomizeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private final static Logger log = LoggerFactory.getLogger(CustomizeAuthenticationSuccessHandler.class);
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
log.info("登录成功!");
User userDetails = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String token = TokenUtil.createToken(userDetails);
JsonResult result = JsonResult.success(userDetails);
httpServletResponse.setContentType("text/json;charsert=utf-8");
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setHeader("token", token);
httpServletResponse.getWriter().write(JSONValue.toJSONString(result));
}
}
5、登录失败处理逻辑 CustomizeAuthenticationFailureHandler
import com.example.office.utils.JsonResult;
import net.minidev.json.JSONValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 登录失败请求处理
*/
@Component
public class CustomizeAuthenticationFailureHandler implements AuthenticationFailureHandler {
private final static Logger log = LoggerFactory.getLogger(CustomizeAuthenticationFailureHandler.class);
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException{
JsonResult result = null;
if (e instanceof BadCredentialsException) {
result = JsonResult.keep("密码错误");
} else if (e instanceof DisabledException) {
result = JsonResult.keep("账号不可用");
} else if (e instanceof InternalAuthenticationServiceException) {
result = JsonResult.keep("账户不存在");
} else {
result = JsonResult.keep("登录失败");
}
log.info("登录失败!" + result.getHint());
httpServletResponse.setContentType("text/json;charset=utf-8");
httpServletResponse.getWriter().write(JSONValue.toJSONString(result));
}
}
6、匿名用户访问无权限资源时的异常处理 CustomizeAuthenticationEntryPoint
import com.example.office.utils.JsonResult;
import net.minidev.json.JSONValue;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 未授权的统一处理方式
*/
@Component
public class CustomizeAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException{
httpServletResponse.setContentType("text/json;charset=utf-8");
JsonResult result = JsonResult.failure("请先登录!", null);
httpServletResponse.getWriter().write(JSONValue.toJSONString(result));
}
}
7、会话失效(账号被挤下线)处理逻辑 CustomizeSessionInformationExpiredStrategy
import com.example.office.utils.JsonResult;
import net.minidev.json.JSONValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.web.session.SessionInformationExpiredEvent;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 会话信息过期策略
*/
@Component
public class CustomizeSessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {
private final static Logger log = LoggerFactory.getLogger(CustomizeSessionInformationExpiredStrategy.class);
@Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent sessionInformationExpiredEvent) throws IOException{
log.info("账号已在其他地方登录!");
JsonResult result = JsonResult.failure("账号已在其他地方登陆", null);
HttpServletResponse httpServletResponse = sessionInformationExpiredEvent.getResponse();
httpServletResponse.setContentType("text/json;charset=utf-8");
httpServletResponse.getWriter().write(JSONValue.toJSONString(result));
}
}
8、登出成功处理逻辑 CustomizeLogoutSuccessHandler
import com.example.office.utils.JsonResult;
import net.minidev.json.JSONValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 登出成功请求处理
*/
@Component
public class CustomizeLogoutSuccessHandler implements LogoutSuccessHandler {
private final static Logger log = LoggerFactory.getLogger(CustomizeLogoutSuccessHandler.class);
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException {
log.info("登出成功!");
JsonResult result = JsonResult.success();
httpServletResponse.setContentType("text/json;charset=utf-8");
httpServletResponse.getWriter().write(JSONValue.toJSONString(result));
}
}
9、UserDetailsService 的实现 UserDetailsServiceImpl
import com.example.office.domain.RoleDomain;
import com.example.office.domain.UsersDomain;
import com.example.office.service.RoleService;
import com.example.office.service.UsersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
public UsersService usersService;
@Autowired
public RoleService roleService;
@Override
@Transactional
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
if ((userName == null) || "".equals(userName)) {
throw new RuntimeException("用户不能为空");
}
UsersDomain user = usersService.findByUserName(userName);
if (user == null) {
throw new RuntimeException("用户不存在!");
}
List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();
List<RoleDomain> list = roleService.findListByUserId(user.getId());
list.forEach(item -> {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(item.getRoleName());
grantedAuthorityList.add(grantedAuthority);
});
return new User(user.getUserName(), user.getPassword(), grantedAuthorityList);
}
}
10、访问权限过滤器 AuthorizationFilter
import com.example.office.utils.JsonResult;
import com.example.office.utils.TokenUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import net.minidev.json.JSONValue;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
/**
* 访问权限过滤器
*/
public class AuthorizationFilter extends BasicAuthenticationFilter {
public AuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
String tokenHeader = request.getHeader(TokenUtil.TOKEN_HEADER);
// 如果请求头中没有token信息则直接放行了
if (!"".equals(tokenHeader)) {
chain.doFilter(request, response);
return;
}
// 如果请求头中有token,则进行解析,并且设置认证信息
try {
SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));
} catch (ExpiredJwtException e) {
//返回json形式的错误信息
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
JsonResult result = JsonResult.failure(e.getMessage(), e);
response.getWriter().write(JSONValue.toJSONString(result));
response.getWriter().flush();
return;
}
super.doFilterInternal(request, response, chain);
}
// 这里从token中获取用户信息并新建一个token
private UsernamePasswordAuthenticationToken getAuthentication(String token) throws ExpiredJwtException {
Claims claims = TokenUtil.verify(token);
boolean expiration = claims.getExpiration().before(new Date());
if (expiration) {
throw new RuntimeException("过期了");
} else {
String username = claims.getSubject();
UserDetails user = (UserDetails)claims.get("user");
if (username != null) {
return new UsernamePasswordAuthenticationToken(username, null,
user.getAuthorities()
);
}
}
return null;
}
}
11、token处理工具类 TokenUtil
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.User;
import java.util.Date;
import java.util.HashMap;
public class TokenUtil {
public static final String TOKEN_HEADER = "token";
private static final long EXPIRE_TIME= 15*60*1000;
private static final String TOKEN_SECRET="tokendemo"; //**盐
private static final String ISS = "echisan";
/**
* 签名生成
* @param user
* @return
*/
public static String createToken(User user){
String token = null;
try {
Date expiresAt = new Date(System.currentTimeMillis() + EXPIRE_TIME);
HashMap<String, Object> map = new HashMap<>();
map.put("user", user);
token = Jwts.builder()
.setIssuer(ISS)
.setClaims(map)
.setSubject(user.getUsername())
.setExpiration(expiresAt)
.setIssuedAt(new Date())
// 使用了HMAC256加密算法。
.signWith(SignatureAlgorithm.HS512, TOKEN_SECRET)
.compact();;
} catch (Exception e){
e.printStackTrace();
}
return token;
}
/**
* 获取
* @param token
* @return
*/
public static Claims verify(String token){
return Jwts.parser().setSigningKey(TOKEN_SECRET).parseClaimsJws(token).getBody();
}
}
12、Aes加密工具类 AesEncryptUtil
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
/**
* 加密和解密
*/
public class AesEncryptUtil {
/**
* key 和 iv 可以随机生成
*/
private static String KEY = "aaDJL2d9DfhLZO0z";
private static String IV = "412ADDSSFA342442";
/**
* 加密
*/
public static String encrypt(String data) {
return encrypt(data, KEY, IV);
}
/**
* 解密
*/
public static String decrypt(String data) {
return decrypt(data, KEY, IV);
}
/**
* 加密方法
*/
private static String encrypt(String data, String key, String iv) {
try {
// "算法/模式/补码方式"NoPadding PkcsPadding
Cipher cipher = Cipher.getInstance("AES/CBC/Nopadding");
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes();
int plaintextLength = dataBytes.length;
if (plaintextLength % blockSize != 0) {
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
}
byte[] plaintext = new byte[plaintextLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintext);
return DatatypeConverter.printBase64Binary(encrypted);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 解密方法
*/
private static String decrypt(String data, String key, String iv) {
try {
byte[] encrypted = DatatypeConverter.parseBase64Binary(data);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] original = cipher.doFinal(encrypted);
return new String(original).trim();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
13、统一消息返回体工具类 JsonResult
public class JsonResult{
public static final int SUCCESS = 0;
public static final int FAILURE = -1;
public static final int KEEP = 1;
private int result;
private Long total;
private String hint;
private Object data;
public JsonResult() {
super();
}
public JsonResult(int result, Object data, Long total, String hint) {
super();
this.result = result;
this.total = total;
this.hint = hint;
this.data = data;
}
/**
* 操作成功,无返回数据
* @return
*/
public static JsonResult success() {
return new JsonResult(SUCCESS, "无返回数据", null, "操作成功");
}
/**
* 操作成功,返回data数据
* @param data
* @return
*/
public static JsonResult success(Object data) {
return new JsonResult(SUCCESS, data, null, "操作成功");
}
/**
* 操作成功,返回data数据及数量
*/
public static JsonResult success(Object data, Long total) {
return new JsonResult(SUCCESS, data, total, "操作成功");
}
/**
* 业务错误
*/
public static JsonResult keep(String hint) {
return new JsonResult(KEEP, null, null, hint);
}
/**
* 程序错误
*/
public static JsonResult failure(String hint, Exception e) {
return new JsonResult(FAILURE, null, null, hint);
}
public int getResult() {
return result;
}
public void setResult(int result) {
this.result = result;
}
public Long getTotal() {
return total;
}
public void setTotal(Long total) {
this.total = total;
}
public String getHint() {
return hint;
}
public void setHint(String hint) {
this.hint = hint;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
14、实体 UsersDomain 字段注解偷懒没加全
import org.hibernate.annotations.GenericGenerator;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@Entity
@Table(name = "USERS")
public class UsersDomain implements Serializable, UserDetails {
private static final long serialVersionUID = 629746947476582411L;
/**
* id
*/
@Id
@GeneratedValue(generator = "idGenerator", strategy = GenerationType.IDENTITY)
@GenericGenerator(name = "idGenerator", strategy = "uuid")
@Column(name = "ID", nullable = false, length = 32)
private String id;
/**
* 用户名
*/
private String userName;
/**
* 密码
*/
private String password;
/**
* 真实姓名
*/
private String realName;
/**
* 账号
*/
private String account;
/**
* 账号是否可用 默认为1 可用
*/
private String isEnabled;
/**
* 创建时间
*/
private Date createTime;
/**
* 修改人
*/
private String createUser;
@OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.REMOVE})
@JoinTable(name = "users_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
private List<RoleDomain> roles;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (RoleDomain role : roles) {
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName()));
}
return authorities;
}
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return userName;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public void setPassword(String password) {
this.password = password;
}
public String getRealName() {
return realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getIsEnabled() {
return isEnabled;
}
public void setIsEnabled(String isEnabled) {
this.isEnabled = isEnabled;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getCreateUser() {
return createUser;
}
public void setCreateUser(String createUser) {
this.createUser = createUser;
}
public List<RoleDomain> getRoles() {
return roles;
}
public void setRoles(List<RoleDomain> roles) {
this.roles = roles;
}
}
15、实体 RoleDomain 字段注解偷懒没加全
import org.hibernate.annotations.GenericGenerator;
import org.springframework.security.core.GrantedAuthority;
import javax.persistence.*;
import java.io.Serializable;
@Table(name = "ROLE")
@Entity
public class RoleDomain implements Serializable, GrantedAuthority {
private static final long serialVersionUID = 620648570340911080L;
/**
* id
*/
@Id
@GeneratedValue(generator = "idGenerator")
@GenericGenerator(name = "idGenerator", strategy = "uuid")
@Column(name = "ID", nullable = false, length = 32)
private String id;
/**
* 角色名
*/
private String roleName;
/**
* 角色说明
*/
private String roleDescription;
@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE})
@JoinTable(name = "users_role", joinColumns = @JoinColumn(name = "role_id"), inverseJoinColumns = @JoinColumn(name = "user_id"))
private UsersDomain users;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleDescription() {
return roleDescription;
}
public void setRoleDescription(String roleDescription) {
this.roleDescription = roleDescription;
}
public UsersDomain getUsers() {
return users;
}
public void setUsers(UsersDomain users) {
this.users = users;
}
@Override
public String getAuthority() {
return this.roleName;
}
}
补充:具体实现类方法不粘了
二、前台
1、main.js
import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import router from './router/index'
import axios from 'axios'
import {Message} from 'element-ui';
import store from "@/store/store";
// 配置公共url
axios.defaults.baseURL = '/api'
//添加请求拦截器
axios.interceptors.request.use(
config =>{
if(store.state.token){
config.headers.common['token'] =store.state.token
}
return config;
},
error =>{
//对请求错误做什么
return Promise.reject(error);
})
//http reponse响应拦截器
axios.interceptors.response.use(
response =>{
return response;
},
error=>{
if(error.response){
switch(error.response.status){
case 401:
localStorage.removeItem('token');
router.replace({
path: '/login',
query: {redirect: router.currentRoute.fullPath}//登录成功后跳入浏览的当前页面
})
}
}
});
Vue.config.productionTip = false
Vue.use(ElementUI)
Vue.prototype.$EventBus = new Vue();
Vue.prototype.$http = axios;
Vue.prototype.$message = Message;
new Vue({
render: h => h(App),
router,
store
}).$mount('#app')
2、router/index.js
import Vue from "vue";
import Router from "vue-router"
Vue.use(Router);
const originalPush = Router.prototype.push
Router.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
}
const layout = () => import('../views/layout/layout')
const router = new Router({
routes: [
{
path: "/",
name: "login",
component: resolve => require(["@/views/login/index"], resolve),
meta: {
keepAlive: true,
mainPart: "登录",
secondaryPart: "登录"
}
},
{
// 首页
path: "/homePage",
name: "homePage",
component: resolve => require(["@/views/homePage"], resolve),
meta: {
keepAlive: true,
mainPart: "首页",
secondaryPart: "首页"
}
},
{
path: "/login",
name: "login",
component: resolve => require(["@/views/login/index"], resolve),
meta: {
keepAlive: true,
mainPart: "登录",
secondaryPart: "登录"
}
}
]
});
// 导航守卫
// 使用router.beforeEach注册一个全局前置守卫,判断用户是否登录
router.beforeEach((to, from, next) => {
if (to.path === '/login' || to.path === '/') {
localStorage.removeItem('token');
next();
} else {
let token = localStorage.getItem('token');
if (token === null || token === '') {
next('/login');
} else {
next();
}
}
});
export default router;
3、store/index.js
// store.js 中都mutation中增加添加和删除token的方法
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = { // 全局管理的数据存储
isLogin:'0',
ser:null,
token:localStorage.getItem('token') ? localStorage.getItem('token'):'', // token
};
export default new Vuex.Store({
state,
getters:{ // 监听数据变化的
getStorage(state){ // 获取本地存储的登录信息
if(!state.token){
// state.token =JSON.parse(localStorage.getItem(key))
state.token =JSON.parse(localStorage.getItem('token'))
}
return state.token
}
},
mutations:{
$_setToken(state, value) { // 设置存储token
state.token = value;
localStorage.setItem('token', value);
},
$_removeStorage(state, value){ // 删除token
localStorage.removeItem('token');
},
}
});
4、AES加密解密方法utils/secret.js
import CryptoJs from 'crypto-js/crypto-js'
const KEY = CryptoJs.enc.Utf8.parse("aaDJL2d9DfhLZO0z");
const IV = CryptoJs.enc.Utf8.parse("412ADDSSFA342442");
// AES加密:字符串 key iv 返回base64
export function Encrypt(word, keyStr, ivStr) {
let key = KEY;
let iv = IV;
if (keyStr) {
key = CryptoJs.enc.Utf8.parse(keyStr);
iv = CryptoJs.enc.Utf8.parse(ivStr);
}
let srcs = CryptoJs.enc.Utf8.parse(word);
var encrypted = CryptoJs.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJs.mode.CBC,
padding:CryptoJs.pad.ZeroPadding
});
return CryptoJs.enc.Base64.stringify(encrypted.ciphertext)
}
/**
* 解密
* @param word
* @param keyStr
* @param ivStr
* @returns {string}
* @constructor
*/
export function Decrypt(word, keyStr, ivStr) {
let key = KEY;
let iv = IV;
if (keyStr) {
key = CryptoJs.enc.Utf8.parse(keyStr);
iv = CryptoJs.enc.Utf8.parse(ivStr);
}
let base64 = CryptoJs.enc.Base64.parse(word);
let src = CryptoJs.enc.Base64.stringify(base64);
let decrypt = CryptoJs.AES.decrypt(src, key, {
iv: iv,
mode: CryptoJs.mode.CBC,
padding:CryptoJs.pad.ZeroPadding
});
let decryptedStr = decrypt.toString(CryptoJs.enc.Utf8);
return decryptedStr.toString();
}
5、登录页 login/index
<el-form :model="loginForm">
<el-form-item label="用户名" prop="username">
<el-input v-model="loginForm.userName"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" v-model="loginForm.password"></el-input>
</el-form-item>
<el-form-item>
<el-button @click="login('loginForm')">登录</el-button>
</el-form-item>
</el-form>
import {Encrypt} from "@/utils/secret";
export default {
name: "index",
data() {
return {
loginForm:{
userName:"",
password:""
}
}
},
methods:{
login() {
const that = this;
this.$http.post('/login', {
username:this.loginForm.userName,
//密码加密传输
password:Encrypt(this.loginForm.password)
}, {headers:{"Content-Type":"application/x-www-form-urlencoded"}, transformRequest: [function (data) {
let ret = ''
for (let it in data) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
return ret
}]
,}).then(function (res) {
if (res.data.result === 0) {
that.$message.success("登录成功!");
that.$store.commit('$_setToken', res.headers.token);
that.$router.push('/homePage');
} else {
that.$message.error("登录失败");
}
})
}
}
}