From cb5849d8a14f55241c44bdf6724b18de7950564d Mon Sep 17 00:00:00 2001 From: panlinlin <648540858@qq.com> Date: 星期三, 14 四月 2021 16:33:10 +0800 Subject: [PATCH] 支持接口鉴权,支持修改密码, --- web_src/src/components/DeviceList.vue | 4 web_src/config/index.js | 3 src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java | 41 +++ src/main/java/com/genersoft/iot/vmp/conf/security/LoginFailureHandler.java | 65 +++++ web_src/src/components/dialog/changePassword.vue | 107 ++++++++ src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java | 52 ++++ src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java | 5 src/main/java/com/genersoft/iot/vmp/service/IUserService.java | 2 pom.xml | 4 web_src/src/components/Login.vue | 4 web_src/src/main.js | 12 src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java | 24 + src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java | 144 +++++++++++ src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java | 24 + src/main/java/com/genersoft/iot/vmp/conf/security/LogoutHandler.java | 27 ++ src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java | 80 ++++++ src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java | 3 src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java | 42 ++ web_src/src/components/UiHeader.vue | 30 ++ src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java | 95 +++++++ 20 files changed, 752 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index c07f7ad..c1383e7 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,10 @@ <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> <!-- druid鏁版嵁搴撹繛鎺ユ睜 --> <dependency> diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java b/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java new file mode 100644 index 0000000..2064470 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java @@ -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("鐢ㄦ埛闇�瑕佺櫥褰曪紝璁块棶[{}]澶辫触锛孉uthenticationException=[{}]", 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(); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java new file mode 100644 index 0000000..c010335 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java @@ -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()); + } + + +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java new file mode 100644 index 0000000..f3fd068 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java @@ -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); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/LoginFailureHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/LoginFailureHandler.java new file mode 100644 index 0000000..1ad05c0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/LoginFailureHandler.java @@ -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)); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java new file mode 100644 index 0000000..9690c6d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java @@ -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); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/LogoutHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/LogoutHandler.java new file mode 100644 index 0000000..790eab8 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/LogoutHandler.java @@ -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); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java b/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java new file mode 100644 index 0000000..d186d84 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java @@ -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杩涜璁よ瘉鎺堟潈 涓诲姩璋� + * 鐢ˋuthenticationManager鐨刟uthenticate鏂规硶瀹炵幇 + * 鎺堟潈鎴愬姛鍚庡皢鐢ㄦ埛淇℃伅瀛樺叆SecurityContext褰撲腑 + * @param username 鐢ㄦ埛鍚� + * @param password 瀵嗙爜 + * @param authenticationManager 璁よ瘉鎺堟潈绠$悊鍣�, + * @see AuthenticationManager + * @return UserInfo 鐢ㄦ埛淇℃伅 + */ + public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException { + //浣跨敤security妗嗘灦鑷甫鐨勯獙璇乼oken鐢熸垚鍣� 涔熷彲浠ヨ嚜瀹氫箟銆� + 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); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java new file mode 100644 index 0000000..1de14e6 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java @@ -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(); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java new file mode 100644 index 0000000..4bb7017 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java @@ -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; + } + + /** + * 鎸囧畾鐢ㄦ埛鏄惁瑙i攣锛岄攣瀹氱殑鐢ㄦ埛鏃犳硶杩涜韬唤楠岃瘉 + * <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(); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IUserService.java b/src/main/java/com/genersoft/iot/vmp/service/IUserService.java index 868118b..cb6b6b7 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IUserService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IUserService.java @@ -8,5 +8,5 @@ boolean changePassword(int id, String password); - + User getUserByUsername(String username); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java index 0a67a01..2539f5b 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java @@ -24,4 +24,9 @@ user.setPassword(password); return userMapper.update(user) > 0; } + + @Override + public User getUserByUsername(String username) { + return userMapper.getUserByUsername(username); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java index 4608a29..e16cadc 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java @@ -28,4 +28,7 @@ @Select("select * FROM user WHERE id= #{id}") User selectById(int id); + + @Select("select * FROM user WHERE username= #{username}") + User getUserByUsername(String username); } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java index cf781c0..4fd7b96 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java @@ -1,5 +1,7 @@ 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.storager.dao.dto.User; import io.swagger.annotations.Api; @@ -8,11 +10,12 @@ import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; 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.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import javax.security.sasl.AuthenticationException; @Api(tags = "鐢ㄦ埛绠$悊") @CrossOrigin @@ -21,21 +24,46 @@ public class UserController { @Autowired - private IUserService userService; + AuthenticationManager authenticationManager; + @Autowired + IUserService userService; @ApiOperation("鐧诲綍") @ApiImplicitParams({ @ApiImplicitParam(name = "username", value = "鐢ㄦ埛鍚�", dataTypeClass = String.class), - @ApiImplicitParam(name = "password", value = "瀵嗙爜锛�32鏈猰d5鍔犲瘑锛�", dataTypeClass = String.class), + @ApiImplicitParam(name = "password", value = "瀵嗙爜锛�32浣峬d5鍔犲瘑锛�", dataTypeClass = String.class), }) @GetMapping("/login") 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) { return "success"; }else { 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"; + } + } } diff --git a/web_src/config/index.js b/web_src/config/index.js index a16611c..e6c0f6c 100644 --- a/web_src/config/index.js +++ b/web_src/config/index.js @@ -17,7 +17,8 @@ pathRewrite: { '^/debug': '/' } - } + }, + }, // Various Dev Server settings diff --git a/web_src/src/components/DeviceList.vue b/web_src/src/components/DeviceList.vue index 40d2146..cdc25bc 100644 --- a/web_src/src/components/DeviceList.vue +++ b/web_src/src/components/DeviceList.vue @@ -215,8 +215,8 @@ console.log(`淇敼浼犺緭鏂瑰紡涓� ${row.streamMode}锛�${row.deviceId} `); let that = this; this.$axios({ - method: 'get', - url: '/api/device/query/transport' + row.deviceId + '/' + row.streamMode + method: 'post', + url: '/api/device/query/transport/' + row.deviceId + '/' + row.streamMode }).then(function(res) { }).catch(function(e) { diff --git a/web_src/src/components/Login.vue b/web_src/src/components/Login.vue index 315293b..65c27f6 100644 --- a/web_src/src/components/Login.vue +++ b/web_src/src/components/Login.vue @@ -63,7 +63,7 @@ this.$axios({ method: 'get', - url:"/api/user/login", + url:"/api/user/login", params: loginParam }).then(function (res) { console.log(JSON.stringify(res)); @@ -81,7 +81,7 @@ }); } }).catch(function (error) { - that.$message.error(error.response.statusText); + that.$message.error(error.response.data.msg); that.isLoging = false; }); }, diff --git a/web_src/src/components/UiHeader.vue b/web_src/src/components/UiHeader.vue index 85bb230..e537953 100644 --- a/web_src/src/components/UiHeader.vue +++ b/web_src/src/components/UiHeader.vue @@ -1,21 +1,30 @@ <template> <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="/deviceList">璁惧鍒楄〃</el-menu-item> <el-menu-item index="/pushVideoList">鎺ㄦ祦鍒楄〃</el-menu-item> <el-menu-item index="/streamProxyList">鎷夋祦浠g悊</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-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> + <changePasswordDialog ref="changePasswordDialog"></changePasswordDialog> </div> </template> <script> + +import changePasswordDialog from './dialog/changePassword.vue' export default { name: "UiHeader", - components: { Notification }, + components: { Notification, changePasswordDialog }, data() { return { alarmNotify: true, @@ -24,10 +33,25 @@ }, methods:{ loginout(){ + this.$axios({ + method: 'get', + url:"/api/user/logout" + }).then((res)=> { // 鍒犻櫎cookie锛屽洖鍒扮櫥褰曢〉闈� this.$cookies.remove("session"); this.$router.push('/login'); 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() { this.sseSource.close(); diff --git a/web_src/src/components/dialog/changePassword.vue b/web_src/src/components/dialog/changePassword.vue new file mode 100644 index 0000000..5842df0 --- /dev/null +++ b/web_src/src/components/dialog/changePassword.vue @@ -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> diff --git a/web_src/src/main.js b/web_src/src/main.js index ed1f36c..56586f5 100644 --- a/web_src/src/main.js +++ b/web_src/src/main.js @@ -40,6 +40,18 @@ 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); -- Gitblit v1.8.0