package me.zhengjie.core.security; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.AllArgsConstructor; import lombok.Getter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.sql.Timestamp; import java.util.*; /** * @author jie * @date 2018-11-23 */ @Getter @AllArgsConstructor public class JwtUser implements UserDetails { @JsonIgnore private final Long id; private final String username; @JsonIgnore private final String password; private final String avatar; private final String email; @JsonIgnore private final Collection<? extends GrantedAuthority> authorities; private final boolean enabled; private Timestamp createTime; @JsonIgnore private final Date lastPasswordResetDate; @JsonIgnore @Override public boolean isAccountNonExpired() { return true; } @JsonIgnore @Override public boolean isAccountNonLocked() { return true; } @JsonIgnore @Override public boolean isCredentialsNonExpired() { return true; } @JsonIgnore @Override public String getPassword() { return password; } @Override public boolean isEnabled() { return enabled; } /** * 在我们保存权限的时候加上了前缀ROLE_,因此在这里需要处理下数据 * @return */ public Collection getRoles() { Set<String> roles = new LinkedHashSet<>(); for (GrantedAuthority authority : authorities) { roles.add(authority.getAuthority().substring(5)); } return roles; } }
package me.zhengjie.core.security; 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; import java.io.Serializable; @Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { /** * 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应 */ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException==null?"Unauthorized":authException.getMessage()); } }
package me.zhengjie.core.security; 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; import java.io.Serializable; @Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { /** * 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应 */ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException==null?"Unauthorized":authException.getMessage()); } }
package me.zhengjie.core.security; import io.jsonwebtoken.ExpiredJwtException; import me.zhengjie.core.utils.JwtTokenUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class JwtAuthorizationTokenFilter extends OncePerRequestFilter { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final UserDetailsService userDetailsService; private final JwtTokenUtil jwtTokenUtil; private final String tokenHeader; public JwtAuthorizationTokenFilter(@Qualifier("jwtUserDetailsService") UserDetailsService userDetailsService, JwtTokenUtil jwtTokenUtil, @Value("${jwt.header}") String tokenHeader) { this.userDetailsService = userDetailsService; this.jwtTokenUtil = jwtTokenUtil; this.tokenHeader = tokenHeader; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { logger.debug("processing authentication for '{}'", request.getRequestURL()); final String requestHeader = request.getHeader(this.tokenHeader); String username = null; String authToken = null; if (requestHeader != null && requestHeader.startsWith("Bearer ")) { authToken = requestHeader.substring(7); try { username = jwtTokenUtil.getUsernameFromToken(authToken); } catch (ExpiredJwtException e) { logger.error(e.getMessage()); } } logger.debug("checking authentication for user '{}'", username); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { logger.debug("security context was null, so authorizating user"); // It is not compelling necessary to load the use details from the database. You could also store the information // in the token and read it from it. It's up to you ;) UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); // For simple validation it is completely sufficient to just check the token integrity. You don't have to call // the database compellingly. Again it's up to you ;) if (jwtTokenUtil.validateToken(authToken, userDetails)) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); logger.info("authorizated user '{}', setting security context", username); SecurityContextHolder.getContext().setAuthentication(authentication); } } chain.doFilter(request, response); } }
package me.zhengjie.core.service; import me.zhengjie.common.exception.EntityNotFoundException; import me.zhengjie.common.utils.ValidationUtil; import me.zhengjie.core.security.JwtUser; import me.zhengjie.system.domain.Permission; import me.zhengjie.system.domain.Role; import me.zhengjie.system.domain.User; import me.zhengjie.system.repository.PermissionRepository; import me.zhengjie.system.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.Cacheable; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * @author jie * @date 2018-11-22 */ @Service @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) public class JwtUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Autowired private PermissionRepository permissionRepository; @Override public UserDetails loadUserByUsername(String username){ User user = null; if(ValidationUtil.isEmail(username)){ user = userRepository.findByEmail(username); } else { user = userRepository.findByUsername(username); } if (user == null) { throw new EntityNotFoundException(User.class, "name", username); } else { return create(user); } } public UserDetails create(User user) { return new JwtUser( user.getId(), user.getUsername(), user.getPassword(), user.getAvatar(), user.getEmail(), mapToGrantedAuthorities(user.getRoles(),permissionRepository), user.getEnabled(), user.getCreateTime(), user.getLastPasswordResetTime() ); } private static List<GrantedAuthority> mapToGrantedAuthorities(Set<Role> roles,PermissionRepository permissionRepository) { Set<Permission> permissions = new HashSet<>(); for (Role role : roles) { Set<Role> roleSet = new HashSet<>(); roleSet.add(role); permissions.addAll(permissionRepository.findByRoles(roleSet)); } return permissions.stream() .map(permission -> new SimpleGrantedAuthority("ROLE_"+permission.getName())) .collect(Collectors.toList()); } }
package me.zhengjie.core.service; import me.zhengjie.common.exception.EntityNotFoundException; import me.zhengjie.common.utils.ValidationUtil; import me.zhengjie.core.security.JwtUser; import me.zhengjie.system.domain.Permission; import me.zhengjie.system.domain.Role; import me.zhengjie.system.domain.User; import me.zhengjie.system.repository.PermissionRepository; import me.zhengjie.system.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.Cacheable; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * @author jie * @date 2018-11-22 */ @Service @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) public class JwtUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Autowired private PermissionRepository permissionRepository; @Override public UserDetails loadUserByUsername(String username){ User user = null; if(ValidationUtil.isEmail(username)){ user = userRepository.findByEmail(username); } else { user = userRepository.findByUsername(username); } if (user == null) { throw new EntityNotFoundException(User.class, "name", username); } else { return create(user); } } public UserDetails create(User user) { return new JwtUser( user.getId(), user.getUsername(), user.getPassword(), user.getAvatar(), user.getEmail(), mapToGrantedAuthorities(user.getRoles(),permissionRepository), user.getEnabled(), user.getCreateTime(), user.getLastPasswordResetTime() ); } private static List<GrantedAuthority> mapToGrantedAuthorities(Set<Role> roles,PermissionRepository permissionRepository) { Set<Permission> permissions = new HashSet<>(); for (Role role : roles) { Set<Role> roleSet = new HashSet<>(); roleSet.add(role); permissions.addAll(permissionRepository.findByRoles(roleSet)); } return permissions.stream() .map(permission -> new SimpleGrantedAuthority("ROLE_"+permission.getName())) .collect(Collectors.toList()); } }
package me.zhengjie.core.rest; import lombok.extern.slf4j.Slf4j; import me.zhengjie.common.aop.log.Log; import me.zhengjie.core.security.AuthenticationToken; import me.zhengjie.core.security.AuthorizationUser; import me.zhengjie.core.utils.JwtTokenUtil; import me.zhengjie.core.security.JwtUser; import me.zhengjie.core.utils.EncryptUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AccountExpiredException; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; /** * @author jie * @date 2018-11-23 * 授权、根据token获取用户详细信息 */ @Slf4j @RestController @RequestMapping("auth") public class AuthenticationController { @Value("${jwt.header}") private String tokenHeader; @Autowired private AuthenticationManager authenticationManager; @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired @Qualifier("jwtUserDetailsService") private UserDetailsService userDetailsService; /** * 登录授权 * @param authorizationUser * @return */ @Log(description = "用户登录") @PostMapping(value = "${jwt.auth.path}") public ResponseEntity<?> authenticationLogin(@RequestBody AuthorizationUser authorizationUser){ final UserDetails userDetails = userDetailsService.loadUserByUsername(authorizationUser.getUsername()); if(!userDetails.getPassword().equals(EncryptUtils.encryptPassword(authorizationUser.getPassword()))){ throw new AccountExpiredException("密码错误"); } if(!userDetails.isEnabled()){ throw new AccountExpiredException("账号已停用,请联系管理员"); } // 生成令牌 final String token = jwtTokenUtil.generateToken(userDetails); // 返回 token return ResponseEntity.ok(new AuthenticationToken(token)); } /** * 获取用户信息 * @param request * @return */ @GetMapping(value = "${jwt.auth.account}") public ResponseEntity getUserInfo(HttpServletRequest request){ JwtUser jwtUser = (JwtUser)userDetailsService.loadUserByUsername(jwtTokenUtil.getUserName(request)); return ResponseEntity.ok(jwtUser); } }
package me.zhengjie.core.security; import lombok.AllArgsConstructor; import lombok.Getter; import java.io.Serializable; /** * @author jie * @date 2018-11-23 * 返回token */ @Getter @AllArgsConstructor public class AuthenticationToken implements Serializable { private final String token; }
package me.zhengjie.core.security; import lombok.Getter; import lombok.Setter; import javax.validation.constraints.NotBlank; /** * @author jie * @date 2018-11-30 */ @Getter @Setter public class AuthorizationUser { @NotBlank private String username; @NotBlank private String password; @Override public String toString() { return "{username=" + username + ", password= ******}"; } }