支持接口鉴权,支持修改密码,
This commit is contained in:
parent
f0afe02154
commit
1ff8c1b9c9
4
pom.xml
4
pom.xml
@ -67,6 +67,10 @@
|
|||||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||||
<version>2.1.4</version>
|
<version>2.1.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- druid数据库连接池 -->
|
<!-- druid数据库连接池 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.genersoft.iot.vmp.conf.security;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
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 AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
|
||||||
|
logger.debug("用户需要登录,访问[{}]失败,AuthenticationException=[{}]", request.getRequestURI(), e.getMessage());
|
||||||
|
// 允许跨域
|
||||||
|
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
// 允许自定义请求头token(允许head跨域)
|
||||||
|
response.setHeader("Access-Control-Allow-Headers", "token, Accept, Origin, X-Requested-With, Content-Type, Last-Modified");
|
||||||
|
response.setHeader("Content-type", "application/json;charset=UTF-8");
|
||||||
|
JSONObject jsonObject = new JSONObject();
|
||||||
|
jsonObject.put("msg", e.getMessage());
|
||||||
|
jsonObject.put("code", "-1");
|
||||||
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
try {
|
||||||
|
response.getWriter().print(jsonObject.toJSONString());
|
||||||
|
} catch (IOException ioException) {
|
||||||
|
ioException.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package com.genersoft.iot.vmp.conf.security;
|
||||||
|
|
||||||
|
import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
|
||||||
|
import com.genersoft.iot.vmp.service.IUserService;
|
||||||
|
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||||
|
import com.github.xiaoymin.knife4j.core.util.StrUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.core.CredentialsContainer;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录认证逻辑
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class DefaultUserDetailsServiceImpl implements UserDetailsService {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IUserService userService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
if (StrUtil.isBlank(username)) {
|
||||||
|
logger.info("登录用户:{} 不存在", username);
|
||||||
|
throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查出密码
|
||||||
|
User user = userService.getUserByUsername(username);
|
||||||
|
String password = SecurityUtils.encryptPassword(user.getPassword());
|
||||||
|
user.setPassword(password);
|
||||||
|
if (user == null) {
|
||||||
|
logger.info("登录用户:{} 不存在", username);
|
||||||
|
throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
|
||||||
|
}
|
||||||
|
return new LoginUser(user, LocalDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.genersoft.iot.vmp.conf.security;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.web.session.InvalidSessionStrategy;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录超时的处理
|
||||||
|
*/
|
||||||
|
public class InvalidSessionHandler implements InvalidSessionStrategy {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(InvalidSessionHandler.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse httpServletResponse) throws IOException, ServletException {
|
||||||
|
String username = request.getParameter("username");
|
||||||
|
logger.info("[登录超时] - [{}]", username);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.genersoft.iot.vmp.conf.security;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.*;
|
||||||
|
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;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class LoginFailureHandler implements AuthenticationFailureHandler {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(LoginFailureHandler.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
|
||||||
|
|
||||||
|
String username = request.getParameter("username");
|
||||||
|
if (e instanceof AccountExpiredException) {
|
||||||
|
// 账号过期
|
||||||
|
logger.info("[登录失败] - 用户[{}]账号过期", username);
|
||||||
|
|
||||||
|
} else if (e instanceof BadCredentialsException) {
|
||||||
|
// 密码错误
|
||||||
|
logger.info("[登录失败] - 用户[{}]密码错误", username);
|
||||||
|
|
||||||
|
} else if (e instanceof CredentialsExpiredException) {
|
||||||
|
// 密码过期
|
||||||
|
logger.info("[登录失败] - 用户[{}]密码过期", username);
|
||||||
|
|
||||||
|
} else if (e instanceof DisabledException) {
|
||||||
|
// 用户被禁用
|
||||||
|
logger.info("[登录失败] - 用户[{}]被禁用", username);
|
||||||
|
|
||||||
|
} else if (e instanceof LockedException) {
|
||||||
|
// 用户被锁定
|
||||||
|
logger.info("[登录失败] - 用户[{}]被锁定", username);
|
||||||
|
|
||||||
|
} else if (e instanceof InternalAuthenticationServiceException) {
|
||||||
|
// 内部错误
|
||||||
|
logger.error(String.format("[登录失败] - [%s]内部错误", username), e);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 其他错误
|
||||||
|
logger.error(String.format("[登录失败] - [%s]其他错误", username), e);
|
||||||
|
}
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("code","0");
|
||||||
|
map.put("msg","登录失败");
|
||||||
|
response.setContentType("application/json;charset=UTF-8");
|
||||||
|
response.getWriter().write(objectMapper.writeValueAsString(map));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.genersoft.iot.vmp.conf.security;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(LoginSuccessHandler.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
|
||||||
|
String username = request.getParameter("username");
|
||||||
|
logger.info("[登录成功] - [{}]", username);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.genersoft.iot.vmp.conf.security;
|
||||||
|
|
||||||
|
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.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录成功
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class LogoutHandler implements LogoutSuccessHandler {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(LogoutHandler.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
|
||||||
|
String username = request.getParameter("username");
|
||||||
|
logger.info("[退出登录成功] - [{}]", username);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package com.genersoft.iot.vmp.conf.security;
|
||||||
|
|
||||||
|
import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
|
||||||
|
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||||
|
import gov.nist.javax.sip.address.UserInfo;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
|
||||||
|
import javax.security.sasl.AuthenticationException;
|
||||||
|
|
||||||
|
public class SecurityUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述根据账号密码进行调用security进行认证授权 主动调
|
||||||
|
* 用AuthenticationManager的authenticate方法实现
|
||||||
|
* 授权成功后将用户信息存入SecurityContext当中
|
||||||
|
* @param username 用户名
|
||||||
|
* @param password 密码
|
||||||
|
* @param authenticationManager 认证授权管理器,
|
||||||
|
* @see AuthenticationManager
|
||||||
|
* @return UserInfo 用户信息
|
||||||
|
*/
|
||||||
|
public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException {
|
||||||
|
//使用security框架自带的验证token生成器 也可以自定义。
|
||||||
|
UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password);
|
||||||
|
Authentication authenticate = authenticationManager.authenticate(token);
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authenticate);
|
||||||
|
LoginUser user = (LoginUser) authenticate.getPrincipal();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录的所有认证信息
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Authentication getAuthentication(){
|
||||||
|
SecurityContext context = SecurityContextHolder.getContext();
|
||||||
|
return context.getAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录用户信息
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static LoginUser getUserInfo(){
|
||||||
|
Authentication authentication = getAuthentication();
|
||||||
|
if(authentication!=null){
|
||||||
|
Object principal = authentication.getPrincipal();
|
||||||
|
if(principal!=null){
|
||||||
|
LoginUser user = (LoginUser) authentication.getPrincipal();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录用户ID
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int getUserId(){
|
||||||
|
LoginUser user = getUserInfo();
|
||||||
|
return user.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成BCryptPasswordEncoder密码
|
||||||
|
*
|
||||||
|
* @param password 密码
|
||||||
|
* @return 加密字符串
|
||||||
|
*/
|
||||||
|
public static String encryptPassword(String password) {
|
||||||
|
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||||
|
return passwordEncoder.encode(password);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
package com.genersoft.iot.vmp.conf.security;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
|
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.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置Spring Security
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||||
|
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DefaultUserDetailsServiceImpl userDetailsService;
|
||||||
|
/**
|
||||||
|
* 登出成功的处理
|
||||||
|
*/
|
||||||
|
@Autowired
|
||||||
|
private LoginFailureHandler loginFailureHandler;
|
||||||
|
/**
|
||||||
|
* 登录成功的处理
|
||||||
|
*/
|
||||||
|
@Autowired
|
||||||
|
private LoginSuccessHandler loginSuccessHandler;
|
||||||
|
/**
|
||||||
|
* 登出成功的处理
|
||||||
|
*/
|
||||||
|
@Autowired
|
||||||
|
private LogoutHandler logoutHandler;
|
||||||
|
/**
|
||||||
|
* 未登录的处理
|
||||||
|
*/
|
||||||
|
@Autowired
|
||||||
|
private AnonymousAuthenticationEntryPoint anonymousAuthenticationEntryPoint;
|
||||||
|
// /**
|
||||||
|
// * 超时处理
|
||||||
|
// */
|
||||||
|
// @Autowired
|
||||||
|
// private InvalidSessionHandler invalidSessionHandler;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 顶号处理
|
||||||
|
// */
|
||||||
|
// @Autowired
|
||||||
|
// private SessionInformationExpiredHandler sessionInformationExpiredHandler;
|
||||||
|
// /**
|
||||||
|
// * 登录用户没有权限访问资源
|
||||||
|
// */
|
||||||
|
// @Autowired
|
||||||
|
// private LoginUserAccessDeniedHandler accessDeniedHandler;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述: 静态资源放行,这里的放行,是不走 Spring Security 过滤器链
|
||||||
|
**/
|
||||||
|
@Override
|
||||||
|
public void configure(WebSecurity web) {
|
||||||
|
// 可以直接访问的静态数据
|
||||||
|
web.ignoring()
|
||||||
|
.antMatchers("/")
|
||||||
|
.antMatchers("/css/**")
|
||||||
|
.antMatchers("/img/**")
|
||||||
|
.antMatchers("/fonts/**")
|
||||||
|
.antMatchers("/index.html")
|
||||||
|
.antMatchers("/doc.html") // "/webjars/**", "/swagger-resources/**", "/v3/api-docs/**"
|
||||||
|
.antMatchers("/webjars/**")
|
||||||
|
.antMatchers("/swagger-resources/**")
|
||||||
|
.antMatchers("/v3/api-docs/**")
|
||||||
|
.antMatchers("/js/**");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置认证方式
|
||||||
|
* @param auth
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
|
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||||
|
// 设置不隐藏 未找到用户异常
|
||||||
|
provider.setHideUserNotFoundExceptions(true);
|
||||||
|
// 用户认证service - 查询数据库的逻辑
|
||||||
|
provider.setUserDetailsService(userDetailsService);
|
||||||
|
// 设置密码加密算法
|
||||||
|
provider.setPasswordEncoder(passwordEncoder());
|
||||||
|
auth.authenticationProvider(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
http.cors().and().csrf().disable();
|
||||||
|
http.authorizeRequests()
|
||||||
|
// 放行接口
|
||||||
|
.antMatchers("/api/user/login","/index/hook/**").permitAll()
|
||||||
|
// 除上面外的所有请求全部需要鉴权认证
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
// 异常处理(权限拒绝、登录失效等)
|
||||||
|
.and().exceptionHandling()
|
||||||
|
.authenticationEntryPoint(anonymousAuthenticationEntryPoint)//匿名用户访问无权限资源时的异常处理
|
||||||
|
// .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源
|
||||||
|
// 登入
|
||||||
|
.and().formLogin().permitAll()//允许所有用户
|
||||||
|
.successHandler(loginSuccessHandler)//登录成功处理逻辑
|
||||||
|
.failureHandler(loginFailureHandler)//登录失败处理逻辑
|
||||||
|
// 登出
|
||||||
|
.and().logout().logoutUrl("/api/user/logout").permitAll()//允许所有用户
|
||||||
|
.logoutSuccessHandler(logoutHandler)//登出成功处理逻辑
|
||||||
|
.deleteCookies("JSESSIONID")
|
||||||
|
// 会话管理
|
||||||
|
// .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 超时处理
|
||||||
|
// .maximumSessions(1)//同一账号同时登录最大用户数
|
||||||
|
// .expiredSessionStrategy(sessionInformationExpiredHandler) // 顶号处理
|
||||||
|
;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述: 密码加密算法 BCrypt 推荐使用
|
||||||
|
**/
|
||||||
|
@Bean
|
||||||
|
public BCryptPasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述: 注入AuthenticationManager管理器
|
||||||
|
**/
|
||||||
|
@Override
|
||||||
|
@Bean
|
||||||
|
public AuthenticationManager authenticationManager() throws Exception {
|
||||||
|
return super.authenticationManager();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package com.genersoft.iot.vmp.conf.security.dto;
|
||||||
|
|
||||||
|
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||||
|
import org.springframework.security.core.CredentialsContainer;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class LoginUser implements UserDetails, CredentialsContainer {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户
|
||||||
|
*/
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime loginTime;
|
||||||
|
|
||||||
|
public LoginUser(User user, LocalDateTime loginTime) {
|
||||||
|
this.user = user;
|
||||||
|
this.loginTime = loginTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPassword() {
|
||||||
|
return user.getPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return user.getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账户是否未过期,过期无法验证
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonExpired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定用户是否解锁,锁定的用户无法进行身份验证
|
||||||
|
* <p>
|
||||||
|
* 密码锁定
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonLocked() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isCredentialsNonExpired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户是否被启用或禁用。禁用的用户无法进行身份验证。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证完成后,擦除密码
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void eraseCredentials() {
|
||||||
|
user.setPassword(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return user.getId();
|
||||||
|
}
|
||||||
|
}
|
@ -8,5 +8,5 @@ public interface IUserService {
|
|||||||
|
|
||||||
boolean changePassword(int id, String password);
|
boolean changePassword(int id, String password);
|
||||||
|
|
||||||
|
User getUserByUsername(String username);
|
||||||
}
|
}
|
||||||
|
@ -24,4 +24,9 @@ public class UserServiceImpl implements IUserService {
|
|||||||
user.setPassword(password);
|
user.setPassword(password);
|
||||||
return userMapper.update(user) > 0;
|
return userMapper.update(user) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getUserByUsername(String username) {
|
||||||
|
return userMapper.getUserByUsername(username);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,4 +28,7 @@ public interface UserMapper {
|
|||||||
|
|
||||||
@Select("select * FROM user WHERE id= #{id}")
|
@Select("select * FROM user WHERE id= #{id}")
|
||||||
User selectById(int id);
|
User selectById(int id);
|
||||||
|
|
||||||
|
@Select("select * FROM user WHERE username= #{username}")
|
||||||
|
User getUserByUsername(String username);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.genersoft.iot.vmp.vmanager.user;
|
package com.genersoft.iot.vmp.vmanager.user;
|
||||||
|
|
||||||
|
import com.genersoft.iot.vmp.conf.security.SecurityUtils;
|
||||||
|
import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
|
||||||
import com.genersoft.iot.vmp.service.IUserService;
|
import com.genersoft.iot.vmp.service.IUserService;
|
||||||
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
@ -8,11 +10,12 @@ import io.swagger.annotations.ApiImplicitParams;
|
|||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.util.DigestUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import javax.security.sasl.AuthenticationException;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
@Api(tags = "用户管理")
|
@Api(tags = "用户管理")
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@ -21,21 +24,46 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
public class UserController {
|
public class UserController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IUserService userService;
|
AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
IUserService userService;
|
||||||
|
|
||||||
@ApiOperation("登录")
|
@ApiOperation("登录")
|
||||||
@ApiImplicitParams({
|
@ApiImplicitParams({
|
||||||
@ApiImplicitParam(name = "username", value = "用户名", dataTypeClass = String.class),
|
@ApiImplicitParam(name = "username", value = "用户名", dataTypeClass = String.class),
|
||||||
@ApiImplicitParam(name = "password", value = "密码(32未md5加密)", dataTypeClass = String.class),
|
@ApiImplicitParam(name = "password", value = "密码(32位md5加密)", dataTypeClass = String.class),
|
||||||
})
|
})
|
||||||
@GetMapping("/login")
|
@GetMapping("/login")
|
||||||
public String login(String username, String password){
|
public String login(String username, String password){
|
||||||
User user = userService.getUser(username, password);
|
LoginUser user = null;
|
||||||
|
try {
|
||||||
|
user = SecurityUtils.login(username, password, authenticationManager);
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return "fail";
|
||||||
|
}
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
return "success";
|
return "success";
|
||||||
}else {
|
}else {
|
||||||
return "fail";
|
return "fail";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation("修改密码")
|
||||||
|
@ApiImplicitParams({
|
||||||
|
@ApiImplicitParam(name = "username", value = "用户名", dataTypeClass = String.class),
|
||||||
|
@ApiImplicitParam(name = "password", value = "密码(未md5加密的密码)", dataTypeClass = String.class),
|
||||||
|
})
|
||||||
|
@PostMapping("/changePassword")
|
||||||
|
public String changePassword(String password){
|
||||||
|
// 获取当前登录用户id
|
||||||
|
int userId = SecurityUtils.getUserId();
|
||||||
|
boolean result = userService.changePassword(userId, DigestUtils.md5DigestAsHex(password.getBytes()));
|
||||||
|
if (result) {
|
||||||
|
return "success";
|
||||||
|
}else {
|
||||||
|
return "fail";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,8 @@ module.exports = {
|
|||||||
pathRewrite: {
|
pathRewrite: {
|
||||||
'^/debug': '/'
|
'^/debug': '/'
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Various Dev Server settings
|
// Various Dev Server settings
|
||||||
|
@ -215,8 +215,8 @@
|
|||||||
console.log(`修改传输方式为 ${row.streamMode}:${row.deviceId} `);
|
console.log(`修改传输方式为 ${row.streamMode}:${row.deviceId} `);
|
||||||
let that = this;
|
let that = this;
|
||||||
this.$axios({
|
this.$axios({
|
||||||
method: 'get',
|
method: 'post',
|
||||||
url: '/api/device/query/transport' + row.deviceId + '/' + row.streamMode
|
url: '/api/device/query/transport/' + row.deviceId + '/' + row.streamMode
|
||||||
}).then(function(res) {
|
}).then(function(res) {
|
||||||
|
|
||||||
}).catch(function(e) {
|
}).catch(function(e) {
|
||||||
|
@ -63,7 +63,7 @@ export default {
|
|||||||
|
|
||||||
this.$axios({
|
this.$axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url:"/api/user/login",
|
url:"/api/user/login",
|
||||||
params: loginParam
|
params: loginParam
|
||||||
}).then(function (res) {
|
}).then(function (res) {
|
||||||
console.log(JSON.stringify(res));
|
console.log(JSON.stringify(res));
|
||||||
@ -81,7 +81,7 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
that.$message.error(error.response.statusText);
|
that.$message.error(error.response.data.msg);
|
||||||
that.isLoging = false;
|
that.isLoging = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -1,21 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="UiHeader">
|
<div id="UiHeader">
|
||||||
<el-menu router :default-active="this.$route.path" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b" mode="horizontal">
|
<el-menu router :default-active="this.$route.path" menu-trigger="click" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b" mode="horizontal">
|
||||||
<el-menu-item index="/">控制台</el-menu-item>
|
<el-menu-item index="/">控制台</el-menu-item>
|
||||||
<el-menu-item index="/deviceList">设备列表</el-menu-item>
|
<el-menu-item index="/deviceList">设备列表</el-menu-item>
|
||||||
<el-menu-item index="/pushVideoList">推流列表</el-menu-item>
|
<el-menu-item index="/pushVideoList">推流列表</el-menu-item>
|
||||||
<el-menu-item index="/streamProxyList">拉流代理</el-menu-item>
|
<el-menu-item index="/streamProxyList">拉流代理</el-menu-item>
|
||||||
<el-menu-item index="/parentPlatformList/15/1">国标级联</el-menu-item>
|
<el-menu-item index="/parentPlatformList/15/1">国标级联</el-menu-item>
|
||||||
|
<el-menu-item @click="openDoc">在线文档</el-menu-item>
|
||||||
<el-switch v-model="alarmNotify" active-text="报警信息推送" style="display: block float: right" @change="sseControl"></el-switch>
|
<el-switch v-model="alarmNotify" active-text="报警信息推送" style="display: block float: right" @change="sseControl"></el-switch>
|
||||||
<el-menu-item style="float: right;" @click="loginout">退出</el-menu-item>
|
<!-- <el-menu-item style="float: right;" @click="loginout">退出</el-menu-item>-->
|
||||||
|
<el-submenu index="" style="float: right;" >
|
||||||
|
<template slot="title">欢迎,{{this.$cookies.get("session").username}}</template>
|
||||||
|
<el-menu-item @click="changePassword">修改密码</el-menu-item>
|
||||||
|
<el-menu-item @click="loginout">注销</el-menu-item>
|
||||||
|
</el-submenu>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
|
<changePasswordDialog ref="changePasswordDialog"></changePasswordDialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import changePasswordDialog from './dialog/changePassword.vue'
|
||||||
export default {
|
export default {
|
||||||
name: "UiHeader",
|
name: "UiHeader",
|
||||||
components: { Notification },
|
components: { Notification, changePasswordDialog },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
alarmNotify: true,
|
alarmNotify: true,
|
||||||
@ -24,10 +33,25 @@ export default {
|
|||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
loginout(){
|
loginout(){
|
||||||
|
this.$axios({
|
||||||
|
method: 'get',
|
||||||
|
url:"/api/user/logout"
|
||||||
|
}).then((res)=> {
|
||||||
// 删除cookie,回到登录页面
|
// 删除cookie,回到登录页面
|
||||||
this.$cookies.remove("session");
|
this.$cookies.remove("session");
|
||||||
this.$router.push('/login');
|
this.$router.push('/login');
|
||||||
this.sseSource.close();
|
this.sseSource.close();
|
||||||
|
}).catch((error)=> {
|
||||||
|
console.error("登出失败")
|
||||||
|
console.error(error)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
changePassword(){
|
||||||
|
this.$refs.changePasswordDialog.openDialog()
|
||||||
|
},
|
||||||
|
openDoc(){
|
||||||
|
console.log(process.env.BASE_API)
|
||||||
|
window.open( !!process.env.BASE_API? process.env.BASE_API + "/doc.html": "/doc.html")
|
||||||
},
|
},
|
||||||
beforeunloadHandler() {
|
beforeunloadHandler() {
|
||||||
this.sseSource.close();
|
this.sseSource.close();
|
||||||
|
107
web_src/src/components/dialog/changePassword.vue
Normal file
107
web_src/src/components/dialog/changePassword.vue
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<div id="changePassword" v-loading="isLoging">
|
||||||
|
<el-dialog
|
||||||
|
title="修改密码"
|
||||||
|
width="40%"
|
||||||
|
top="2rem"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:visible.sync="showDialog"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
@close="close()"
|
||||||
|
>
|
||||||
|
<div id="shared" style="margin-right: 20px;">
|
||||||
|
<el-form ref="passwordForm" :rules="rules" status-icon label-width="80px">
|
||||||
|
<el-form-item label="新密码" prop="newPassword" >
|
||||||
|
<el-input v-model="newPassword" autocomplete="off"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="确认密码" prop="confirmPassword">
|
||||||
|
<el-input v-model="confirmPassword" autocomplete="off"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<div style="float: right;">
|
||||||
|
<el-button type="primary" @click="onSubmit">保存</el-button>
|
||||||
|
<el-button @click="close">取消</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "changePassword",
|
||||||
|
props: {},
|
||||||
|
computed: {},
|
||||||
|
created() {},
|
||||||
|
data() {
|
||||||
|
let validatePass = (rule, value, callback) => {
|
||||||
|
if (value === '') {
|
||||||
|
callback(new Error('请输入密码'));
|
||||||
|
} else {
|
||||||
|
if (this.confirmPassword !== '') {
|
||||||
|
this.$refs.passwordForm.validateField('confirmPassword');
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let validatePass2 = (rule, value, callback) => {
|
||||||
|
if (this.confirmPassword === '') {
|
||||||
|
callback(new Error('请再次输入密码'));
|
||||||
|
} else if (this.confirmPassword !== this.newPassword) {
|
||||||
|
callback(new Error('两次输入密码不一致!'));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
newPassword: null,
|
||||||
|
confirmPassword: null,
|
||||||
|
showDialog: false,
|
||||||
|
isLoging: false,
|
||||||
|
rules: {
|
||||||
|
newPassword: [{ required: true, validator: validatePass, trigger: "blur" }],
|
||||||
|
confirmPassword: [{ required: true, validator: validatePass2, trigger: "blur" }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openDialog: function () {
|
||||||
|
this.showDialog = true;
|
||||||
|
},
|
||||||
|
onSubmit: function () {
|
||||||
|
this.$axios({
|
||||||
|
method: 'post',
|
||||||
|
url:"/api/user/changePassword",
|
||||||
|
params: {
|
||||||
|
password: this.newPassword
|
||||||
|
}
|
||||||
|
}).then((res)=> {
|
||||||
|
if (res.data === "success"){
|
||||||
|
this.$message({
|
||||||
|
showClose: true,
|
||||||
|
message: '修改成功,请重新登陆',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
this.showDialog = false;
|
||||||
|
setTimeout(()=>{
|
||||||
|
// 删除cookie,回到登录页面
|
||||||
|
this.$cookies.remove("session");
|
||||||
|
this.$router.push('/login');
|
||||||
|
this.sseSource.close();
|
||||||
|
},800)
|
||||||
|
}
|
||||||
|
}).catch((error)=> {
|
||||||
|
console.error(error)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
close: function () {
|
||||||
|
this.showDialog = false;
|
||||||
|
this.newPassword= null;
|
||||||
|
this.confirmPassword=null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
@ -40,6 +40,18 @@ Vue.prototype.$notify = Notification;
|
|||||||
|
|
||||||
axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : "";
|
axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : "";
|
||||||
|
|
||||||
|
// api 返回401自动回登陆页面
|
||||||
|
axios.interceptors.response.use(function (response) {
|
||||||
|
// 对响应数据做点什么
|
||||||
|
return response;
|
||||||
|
}, function (error) {
|
||||||
|
// 对响应错误做点什么
|
||||||
|
if (error.response.status === 401) {
|
||||||
|
router.push('/login');
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
Vue.prototype.$cookies.config(60*30);
|
Vue.prototype.$cookies.config(60*30);
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user