社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
1、配置pom文件,添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2、配置application.properties文件
#JWT
# 签发者
jwt.issuer=Augmentum
jwt.header=Authorization
# 过期时间(-1永不过期)
jwt.expires_in=-1
#jwt.expires_in=86400
# 密匙
jwt.secret=gvdlju
jwt.cookie=AUTH-TOKEN
3、项目结构如下
4、添加用户实体类以及相关操作的类
/**
* @author user
*/
@Entity
@Table(name = "tb_user")
public class User {
@Id
@GeneratedValue
private long id;
private String username;
private String password;
public long getId() {
return id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
/**
* The interface User repository.
*
* @author user
*/
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
@Query(value = "select user from User user where user.username=?1 and user.password=?2")
User findByUsernameAndPassword(String username, String password);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserRepository userRepository;
@Override
public User findByUserName(String userName) {
return userRepository.findByUsername(userName);
}
@Override
public User findByUserNameAndPassword(String userName, String password) {
return userRepository.findByUsernameAndPassword(userName, password);
}
@Override
public User signUp(User user) {
return userRepository.save(user);
}
}
5、创建TokenHelper工具类生成/解析Token
@Component
public class TokenHelper {
@Value("${jwt.issuer}")
private String ISSUER;
@Value("${jwt.secret}")
private String SECRET;
@Value("${jwt.expires_in}")
private int EXPIRES_IN;
@Value("${jwt.header}")
private String AUTH_HEADER;
@Value("${jwt.cookie}")
private String AUTH_COOKIE;
@Autowired
UserService userService;
private Logger logger = LoggerFactory.getLogger(this.getClass());
// 数字签名算法
private SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS512;
/**
* 从Token中取得用户名
* @param token
* @return
* @throws TokenInvalidException
* @throws TokenException
*/
public String getUsernameFromToken(String token) throws TokenInvalidException, TokenException {
final Claims claims = this.getClaimsFromToken(token);
String username = claims.getSubject();
return username;
}
/**
* 根据用户名生成Token
* @param username
* @return
*/
public String generateToken(String username) {
return Jwts.builder().setIssuer(ISSUER).setSubject(username).setIssuedAt(generateCurrentDate())
.setExpiration(generateExpirationDate()).signWith(SIGNATURE_ALGORITHM, SECRET).compact();
}
private Claims getClaimsFromToken(String token) throws TokenInvalidException, TokenException {
Claims claims;
try {
claims = Jwts.parser().setSigningKey(this.SECRET).parseClaimsJws(token).getBody();
} catch (ExpiredJwtException e) {
logger.error(e.getMessage(), e);
throw new TokenException(ExceptionEnum.TOKEN_ERROR_EXPIRED.getCode(), ExceptionEnum.TOKEN_ERROR_EXPIRED.getMessage());
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new TokenInvalidException(token);
}
return claims;
}
String generateToken(Map<String, Object> claims) {
return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate())
.signWith(SIGNATURE_ALGORITHM, SECRET).compact();
}
public Boolean canTokenBeRefreshed(String token) {
try {
final Date expirationDate = getClaimsFromToken(token).getExpiration();
String username = getUsernameFromToken(token);
User user = userService.findByUserName(username);
if (user != null && expirationDate.compareTo(generateCurrentDate()) > 0) {
return true;
}
return false;
} catch (Exception e) {
logger.error(e.getMessage(), e);
return false;
}
}
public String refreshToken(String token) {
String refreshedToken;
try {
final Claims claims = getClaimsFromToken(token);
claims.setIssuedAt(generateCurrentDate());
refreshedToken = generateToken(claims);
} catch (Exception e) {
logger.error(e.getMessage(), e);
refreshedToken = null;
}
return refreshedToken;
}
private long getCurrentTimeMillis() {
// return DateTime.now().getMillis();
return System.currentTimeMillis();
}
private Date generateCurrentDate() {
return new Date(getCurrentTimeMillis());
}
private Date generateExpirationDate() {
if ( this.EXPIRES_IN == -1) {
return null;
}
logger.info("EXPIRES_IN" + EXPIRES_IN);
logger.info(new Date(getCurrentTimeMillis() + this.EXPIRES_IN * 1000) + "///");
return new Date(getCurrentTimeMillis() + this.EXPIRES_IN * 1000);
}
public String getToken(HttpServletRequest request) {
/**
* Getting the token from Cookie store
*/
Cookie authCookie = getCookieValueByName(request, AUTH_COOKIE);
if (authCookie != null) {
return authCookie.getValue();
}
/**
* Getting the token from Authentication header e.g Bearer your_token
*/
String authHeader = request.getHeader(AUTH_HEADER);
if (authHeader != null && authHeader.startsWith("Bearer ")) {
return authHeader.substring(7);
}
return null;
}
/**
* Find a specific HTTP cookie in a request.
*
* @param request
* The HTTP request object.
* @param name
* The cookie name to look for.
* @return The cookie, or <code>null</code> if not found.
*/
public Cookie getCookieValueByName(HttpServletRequest request, String name) {
if (request.getCookies() == null) {
return null;
}
for (int i = 0; i < request.getCookies().length; i++) {
if (request.getCookies()[i].getName().equals(name)) {
return request.getCookies()[i];
}
}
return null;
}
/**
* 将User转换为JwtUser
* @param user
* @return
*/
public JwtUser convertUserToJwtUser(User user) {
JwtUser jwtUser = new JwtUser(user.getId(), user.getUsername(), getAuthorities(user));
return jwtUser;
}
/**
* 得到User的权限
* @param user
* @return
*/
public List<AuthorityInfo> getAuthorities(User user) {
List<AuthorityInfo> authorities = new ArrayList<>();
// 添加权限--这里添加了admin权限
authorities.add(new AuthorityInfo(Authority.ADMIN.getAuthorityName()));
return authorities;
}
}
6、实现GrantedAuthority接口,设置用户权限
public class AuthorityInfo implements GrantedAuthority {
private static final long serialVersionUID = 1L;
private String authority;
public AuthorityInfo(String authority) {
this.authority = authority;
}
@Override
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
}
public enum Authority {
ADMIN("admin"), USER("user");
private String authorityName;
Authority(String authorityName) {
this.authorityName = authorityName;
}
public String getAuthorityName() {
return this.authorityName;
}
}
7、实现UserDetailsService接口返回UserDetails用户信息
public class JwtUser implements UserDetails {
private static final long serialVersionUID = 1L;
private long id;
private String userName;
private List<AuthorityInfo> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return this.userName;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public JwtUser(long id, String userName, List<AuthorityInfo> authorities) {
super();
this.id = id;
this.userName = userName;
this.authorities = authorities;
}
public JwtUser() {
super();
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setAuthorities(List<AuthorityInfo> authorities) {
this.authorities = authorities;
}
}
@Service
public class JwtCustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Autowired
private TokenHelper tokenHelper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username));
} else {
return tokenHelper.convertUserToJwtUser(user);
}
}
}
8、过滤Token,认证UserDetails并生成认证通过的Authentication存放进SecurityContextHolder
/**
* Token拦截器,token过滤器来验证token有效性
* 从http头的Authorization 项读取token数据,然后用Jwts包提供的方法校验token的合法性。
* 如果校验通过,就认为这是一个取得授权的合法请求
* @author user
*/
public class TokenAuthenticationFilter extends OncePerRequestFilter {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
TokenHelper tokenHelper;
@Autowired
JwtCustomUserDetailsService jwtCustomUserDetailsService;
@Autowired
HttpUtil httpUtil;
/**
* 对请求进行过滤,验证token的有效性,
* 如果有效将用户信息存入spring security context中
*/
@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String authToken = tokenHelper.getToken(request);
if (StringUtils.isEmpty(authToken) || authToken.equals("null")) {
//authToken为空的处理
TokenException tokenEmptyException = new TokenException(ExceptionEnum.TOKEN_ERROR_EMPTY.getCode(), ExceptionEnum.TOKEN_ERROR_EMPTY.getMessage());
httpUtil.returnResponseBody(request, response, tokenEmptyException);
return;
}
logger.info("authToken:::" + authToken);
String username = null;
try {
username = tokenHelper.getUsernameFromToken(authToken);
} catch (TokenException e) {
logger.error(e.getMessage(), e);
TokenException tokenExpiredException = new TokenException(ExceptionEnum.TOKEN_ERROR_EXPIRED.getCode(), ExceptionEnum.TOKEN_ERROR_EXPIRED.getMessage());
httpUtil.returnResponseBody(request, response, tokenExpiredException);
return;
} catch (TokenInvalidException e) {
logger.error(e.getMessage(), e);
TokenInvalidException tokenInvalidException = new TokenInvalidException(authToken);
httpUtil.returnResponseBody(request, response, tokenInvalidException);
return;
}
UserDetails userDetails = jwtCustomUserDetailsService.loadUserByUsername(username);
if (userDetails == null) {
logger.error("Invalid Token error with username: " + username);
TokenInvalidException tokenInvalidException = new TokenInvalidException(authToken);
httpUtil.returnResponseBody(request, response, tokenInvalidException);
return;
}
// create authentication
TokenBasedAuthentication authentication = new TokenBasedAuthentication(userDetails);
authentication.setToken(authToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
/**
* 配置不经过filter的请求
*/
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
final String APP_REST_MATCHER = "/oauth/**";
final String SWAGGER_UI = "/swagger-ui.html";
final String SWAGGER_RESOURCES = "/swagger-resources/**";
final String SWAGGER_IMAGES = "/images/**";
final String SWAGGER_WEBJARS = "/webjars/**";
final String SWAGGER_V2 = "/v2/api-docs";
final String SWAGGER_CONF_UI = "/configuration/ui";
final String SWAGGER_CONF_SEC = "/configuration/security";
List<String> pathsToSkip = Arrays.asList(APP_REST_MATCHER, SWAGGER_UI, SWAGGER_RESOURCES, SWAGGER_IMAGES, SWAGGER_WEBJARS, SWAGGER_V2, SWAGGER_CONF_UI, SWAGGER_CONF_SEC);
// web端api不需要验证token,只需走cas逻辑(如请求满足跳过验证条件)
return skipPathRequest(request, pathsToSkip);
}
/**
* 判断请求是否跳过验证
* @param request 用户发送的请求
* @param pathsToSkip 可跳过验证的请求
* @return
*/
private boolean skipPathRequest(HttpServletRequest request, List<String> pathsToSkip) {
Assert.notNull(pathsToSkip, "pathsToSkip is null!");
List<RequestMatcher> m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path))
.collect(Collectors.toList());
OrRequestMatcher matchers = new OrRequestMatcher(m);
return matchers.matches(request);
}
}
public class TokenBasedAuthentication extends AbstractAuthenticationToken {
private static final long serialVersionUID = 1L;
private String token;
private final UserDetails principle;
public TokenBasedAuthentication(UserDetails principle ) {
super( principle.getAuthorities() );
this.principle = principle;
}
public String getToken() {
return token;
}
public void setToken(String token ) {
this.token = token;
}
@Override
public boolean isAuthenticated() {
return true;
}
@Override
public Object getCredentials() {
return token;
}
@Override
public UserDetails getPrincipal() {
return principle;
}
}
9、自定义认证失败处理类,然后配置Security的认证策略
/**
* 认证失败处理类
* @author user
*/
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
}
}
/**
* SpringSecurity的配置
* 配置Security的认证策略
* @author user
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public TokenAuthenticationFilter jwtAuthenticationTokenFilter() throws Exception {
return new TokenAuthenticationFilter();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
private JwtCustomUserDetailsService customUserDetailsService;
@Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
// 由于使用的是JWT,我们这里不需要csrf
.csrf().disable()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().
// 添加认证失败处理类
exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint).and()
.authorizeRequests()
// 对于获取token的rest api要允许匿名访问
.antMatchers("/oauth/**").permitAll()
.antMatchers("/static/**").permitAll()
// swagger start
.antMatchers("/swagger-ui.html").permitAll()
.antMatchers("/swagger-resources/**").permitAll()
.antMatchers("/images/**").permitAll()
.antMatchers("/webjars/**").permitAll()
.antMatchers("/v2/api-docs").permitAll()
.antMatchers("/configuration/ui").permitAll()
.antMatchers("/configuration/security").permitAll()
// swagger end
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
http
// 添加JWT filter
.addFilterBefore(jwtAuthenticationTokenFilter(), BasicAuthenticationFilter.class);
}
}
10、Controller层测试
@RestController
@RequestMapping(value = "/oauth")
public class OauthController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private TokenHelper tokenHelper;
@Autowired
private UserService userService;
/**
* 注册用户,无需验证token
* @param user
*/
@PostMapping("/signup")
public void signUp(@RequestBody User user) {
userService.signUp(user);
}
/**
* 登录并返回jwt生成的token
* @param user
* @return
* @throws Exception
*/
@RequestMapping(value = "/login", method = RequestMethod.POST)
public JsonResponse<String> loginWeChat(@RequestBody User user) throws Exception {
User existUser = userService.findByUserNameAndPassword(user.getUsername(), user.getPassword());
if (existUser == null) {
throw new UserNameOrPasswordIncorrectException();
}
logger.info("登录:::" + user.getUsername());
String token = tokenHelper.generateToken(user.getUsername());
return new JsonResponse<String>(true, token);
}
}
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/test")
public String test() {
System.out.println("test");
return "hello";
}
}
请求验证:访问/users/test
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!