<!-- spring security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- jwt --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
package com.louis.mango.admin.controller; import java.awt.image.BufferedImage; import java.io.IOException; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import com.google.code.kaptcha.Constants; import com.google.code.kaptcha.Producer; import com.louis.mango.admin.model.SysUser; import com.louis.mango.admin.security.JwtAuthenticatioToken; import com.louis.mango.admin.service.SysUserService; import com.louis.mango.admin.util.PasswordUtils; import com.louis.mango.admin.util.SecurityUtils; import com.louis.mango.admin.vo.LoginBean; import com.louis.mango.common.utils.IOUtils; import com.louis.mango.core.http.HttpResult; /** * 登录控制器 * * @author Louis * @date Jan 14, 2019 */ @RestController public class SysLoginController { @Autowired private Producer producer; @Autowired private SysUserService sysUserService; @Autowired private AuthenticationManager authenticationManager; @GetMapping("captcha.jpg") public void captcha(HttpServletResponse response, HttpServletRequest request) throws ServletException, IOException { response.setHeader("Cache-Control", "no-store, no-cache"); response.setContentType("image/jpeg"); // 生成文字验证码 String text = producer.createText(); // 生成图片验证码 BufferedImage image = producer.createImage(text); // 保存到验证码到 session request.getSession().setAttribute(Constants.KAPTCHA_SESSION_KEY, text); ServletOutputStream out = response.getOutputStream(); ImageIO.write(image, "jpg", out); IOUtils.closeQuietly(out); } /** * 登录接口 */ @PostMapping(value = "/login") public HttpResult login(@RequestBody LoginBean loginBean, HttpServletRequest request) throws IOException { String username = loginBean.getAccount(); String password = loginBean.getPassword(); String captcha = loginBean.getCaptcha(); // 从session中获取之前保存的验证码跟前台传来的验证码进行匹配 Object kaptcha = request.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY); if (kaptcha == null) { return HttpResult.error("验证码已失效"); } if (!captcha.equals(kaptcha)) { return HttpResult.error("验证码不正确"); } // 用户信息 SysUser user = sysUserService.findByName(username); // 账号不存在、密码错误 if (user == null) { return HttpResult.error("账号不存在"); } if (!PasswordUtils.matches(user.getSalt(), password, user.getPassword())) { return HttpResult.error("密码不正确"); } // 账号锁定 if (user.getStatus() == 0) { return HttpResult.error("账号已被锁定,请联系管理员"); } // 系统登录认证 JwtAuthenticatioToken token = SecurityUtils.login(request, username, password, authenticationManager); return HttpResult.ok(token); } }
package com.louis.mango.admin.util; import javax.servlet.http.HttpServletRequest; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import com.louis.mango.admin.security.JwtAuthenticatioToken; /** * Security相关操作 * @author Louis * @date Jan 14, 2019 */ public class SecurityUtils { /** * 系统登录认证 * @param request * @param username * @param password * @param authenticationManager * @return */ public static JwtAuthenticatioToken login(HttpServletRequest request, String username, String password, AuthenticationManager authenticationManager) { JwtAuthenticatioToken token = new JwtAuthenticatioToken(username, password); token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); // 执行登录认证过程 Authentication authentication = authenticationManager.authenticate(token); // 认证成功存储认证信息到上下文 SecurityContextHolder.getContext().setAuthentication(authentication); // 生成令牌并返回给客户端 token.setToken(JwtTokenUtils.generateToken(authentication)); return token; } /** * 获取令牌进行认证 * @param request */ public static void checkAuthentication(HttpServletRequest request) { // 获取令牌并根据令牌获取登录认证信息 Authentication authentication = JwtTokenUtils.getAuthenticationeFromToken(request); // 设置登录认证信息到上下文 SecurityContextHolder.getContext().setAuthentication(authentication); } /** * 获取当前用户名 * @return */ public static String getUsername() { String username = null; Authentication authentication = getAuthentication(); if(authentication != null) { Object principal = authentication.getPrincipal(); if(principal != null && principal instanceof UserDetails) { username = ((UserDetails) principal).getUsername(); } } return username; } /** * 获取用户名 * @return */ public static String getUsername(Authentication authentication) { String username = null; if(authentication != null) { Object principal = authentication.getPrincipal(); if(principal != null && principal instanceof UserDetails) { username = ((UserDetails) principal).getUsername(); } } return username; } /** * 获取当前登录信息 * @return */ public static Authentication getAuthentication() { if(SecurityContextHolder.getContext() == null) { return null; } Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return authentication; } }
package com.louis.mango.admin.util; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import com.louis.mango.admin.security.GrantedAuthorityImpl; import com.louis.mango.admin.security.JwtAuthenticatioToken; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; /** * JWT工具类 * @author Louis * @date Jan 14, 2019 */ public class JwtTokenUtils implements Serializable { private static final long serialVersionUID = 1L; /** * 用户名称 */ private static final String USERNAME = Claims.SUBJECT; /** * 创建时间 */ private static final String CREATED = "created"; /** * 权限列表 */ private static final String AUTHORITIES = "authorities"; /** * 密钥 */ private static final String SECRET = "abcdefgh"; /** * 有效期12小时 */ private static final long EXPIRE_TIME = 12 * 60 * 60 * 1000; /** * 生成令牌 * * @param userDetails 用户 * @return 令牌 */ public static String generateToken(Authentication authentication) { Map<String, Object> claims = new HashMap<>(3); claims.put(USERNAME, SecurityUtils.getUsername(authentication)); claims.put(CREATED, new Date()); claims.put(AUTHORITIES, authentication.getAuthorities()); return generateToken(claims); } /** * 从数据声明生成令牌 * * @param claims 数据声明 * @return 令牌 */ private static String generateToken(Map<String, Object> claims) { Date expirationDate = new Date(System.currentTimeMillis() + EXPIRE_TIME); return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, SECRET).compact(); } /** * 从令牌中获取用户名 * * @param token 令牌 * @return 用户名 */ public static String getUsernameFromToken(String token) { String username; try { Claims claims = getClaimsFromToken(token); username = claims.getSubject(); } catch (Exception e) { username = null; } return username; } /** * 根据请求令牌获取登录认证信息 * @param token 令牌 * @return 用户名 */ public static Authentication getAuthenticationeFromToken(HttpServletRequest request) { Authentication authentication = null; // 获取请求携带的令牌 String token = JwtTokenUtils.getToken(request); if(token != null) { // 请求令牌不能为空 if(SecurityUtils.getAuthentication() == null) { // 上下文中Authentication为空 Claims claims = getClaimsFromToken(token); if(claims == null) { return null; } String username = claims.getSubject(); if(username == null) { return null; } if(isTokenExpired(token)) { return null; } Object authors = claims.get(AUTHORITIES); List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); if (authors != null && authors instanceof List) { for (Object object : (List) authors) { authorities.add(new GrantedAuthorityImpl((String) ((Map) object).get("authority"))); } } authentication = new JwtAuthenticatioToken(username, null, authorities, token); } else { if(validateToken(token, SecurityUtils.getUsername())) { // 如果上下文中Authentication非空,且请求令牌合法,直接返回当前登录认证信息 authentication = SecurityUtils.getAuthentication(); } } } return authentication; } /** * 从令牌中获取数据声明 * * @param token 令牌 * @return 数据声明 */ private static Claims getClaimsFromToken(String token) { Claims claims; try { claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody(); } catch (Exception e) { claims = null; } return claims; } /** * 验证令牌 * @param token * @param username * @return */ public static Boolean validateToken(String token, String username) { String userName = getUsernameFromToken(token); return (userName.equals(username) && !isTokenExpired(token)); } /** * 刷新令牌 * @param token * @return */ public static String refreshToken(String token) { String refreshedToken; try { Claims claims = getClaimsFromToken(token); claims.put(CREATED, new Date()); refreshedToken = generateToken(claims); } catch (Exception e) { refreshedToken = null; } return refreshedToken; } /** * 判断令牌是否过期 * * @param token 令牌 * @return 是否过期 */ public static Boolean isTokenExpired(String token) { try { Claims claims = getClaimsFromToken(token); Date expiration = claims.getExpiration(); return expiration.before(new Date()); } catch (Exception e) { return false; } } /** * 获取请求token * @param request * @return */ public static String getToken(HttpServletRequest request) { String token = request.getHeader("Authorization"); String tokenHead = "Bearer "; if(token == null) { token = request.getHeader("token"); } else if(token.contains(tokenHead)){ token = token.substring(tokenHead.length()); } if("".equals(token)) { token = null; } return token; } }
package com.louis.mango.admin.vo; /** * 登录接口封装对象 * @author Louis * @date Oct 29, 2018 */ public class LoginBean { private String account; private String password; private String captcha; public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getCaptcha() { return captcha; } public void setCaptcha(String captcha) { this.captcha = captcha; } }