一、使用security默认登录接口登录成功、生成token、返回前段
项目的结构如下:
1.引入jar包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- alibaba json --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.41</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- JWT --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.7.0</version> </dependency> <!--swagger相关-start--> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> <version>1.5.19</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <!--swagger相关-end-->测试的用户实体
@Data public class User { private String id; private String name; private String password; private boolean dev; private boolean channel; public User() { } public User(String id, String name, String password) { this.id = id; this.name = name; this.password = password; } }Jwt生成token,拦截和验证token的合法性
package com.example.security.Jwt; import com.example.security.token.TokenManager; import com.example.security.util.SpringContextUtil; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * Created by zhicheng.zhao on 2020/9/4. */ public class JwtAuthFilter extends BasicAuthenticationFilter { private TokenManager tokenManager; public JwtAuthFilter(AuthenticationManager authenticationManager, TokenManager tokenManager) { super(authenticationManager); this.tokenManager = tokenManager; } /** * 过滤器 * * @param request * @param response * @param chain * @throws IOException * @throws ServletException */ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { // 设置请求权限 response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin")); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS,DELETE,PATCH"); response.setHeader("Access-Control-Allow-Headers", "tokenId,userType,token,Origin,X-Requested-With,Content-Type,Accept,appKey,hszCookie"); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json,charset=utf-8"); String url = request.getRequestURI(); // 鉴权判断:本地环境模拟管理员权限,其他环境需要根据url鉴权 zzc System.out.println(SpringContextUtil.getProp("authVerify")); if (SpringContextUtil.getProp("authVerify")==null?false:SpringContextUtil.getProp("authVerify").equalsIgnoreCase("false1")) { // 本地环境,存储用户认证信息 String tokenTmp = tokenManager.createTokenAll("test"); UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(tokenTmp); SecurityContextHolder.getContext().setAuthentication(authenticationToken); chain.doFilter(request, response); return; } else if (urlFilter(url)) { chain.doFilter(request, response); return; } // 过滤token为空 String token = request.getHeader("token"); if (token == null || "".equals(token)) { response.setHeader("Content-Type", "application/json"); response.getWriter().print("token为空!"); return; } // 从token获取用户认证信息 UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(token); if (authenticationToken == null) { response.setHeader("Content-Type", "application/json"); response.getWriter().print("token信息错误!"); return; } // 存储用户认证信息 SecurityContextHolder.getContext().setAuthentication(authenticationToken); chain.doFilter(request, response); } /** * 不需要token的地址: * 用户名登录/手机登录/第三方登录/微信登录 * 注册 * 登录前(authFree) * * @param url * @return */ private boolean urlFilter(String url) { return url.contains("/login") || url.contains("Login") || url.contains("/regist") || url.contains("Regist") || url.contains("/authFree"); } /** * 从token获取用户认证信息 * * @param token * @return */ private UsernamePasswordAuthenticationToken getAuthentication(String token) { try { Claims claims = Jwts.parser() .setSigningKey("JwtSecret") .parseClaimsJws(token) .getBody(); String user = claims.getSubject(); String role = claims.get("roles").toString(); List<SimpleGrantedAuthority> userRolesByToken = Arrays.stream(role.split(",")) .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); if (!user.isEmpty()) { return new UsernamePasswordAuthenticationToken(user, null, userRolesByToken); } } catch (Exception e) { logger.info(e.getMessage()); } return null; } }查询数据库用户信息的服务
@Service public class UserService { /** * @Description: 这里应该写的是调用Dao层,根据主键查询出用户,由于我没有配置直接返回用户,注意,密码是经过加密的 * @author: YunZhao.Wang * @date: 2020/10/21 10:26 * @version: */ public User getUser(String name){ User user = new User("1","test", new BCryptPasswordEncoder().encode("123")) ; if (user.getName().equals(name)){ return user; }else { return new User(); } } }实现security底层查询用户信息的接口,让security的底层查询数据的用户信息
@Service public class CustomUserService implements UserDetailsService { @Autowired private UserService userService; @Override public UserDetails loadUserByUsername(String account) throws UsernameNotFoundException { //账号信息 com.example.security.model.User accountInfo = userService.getUser(account); if (accountInfo == null) throw new UsernameNotFoundException("Account[" + account + "]not found"); List<SimpleGrantedAuthority> authorities = new ArrayList<>(); //对应的权限添加 authorities.add(new SimpleGrantedAuthority("ROLE_USER")); return new User(accountInfo.getName(), accountInfo.getPassword(), authorities); } }集成 WebSecurityConfigurerAdapter,重写configure接口
package com.example.security.security; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.example.security.Jwt.JwtAuthFilter; import com.example.security.model.User; import com.example.security.token.TokenManager; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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.config.http.SessionCreationPolicy; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @program: security进行登录 * @description: Security * @author: YunZhao.Wang * @create: 2020-10-21 10:29 **/ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomUserService customUserService; @Autowired private TokenManager tokenManager; @Override public void configure(HttpSecurity http) throws Exception { //关闭csrf保护 http.csrf().disable(); // 无状态 http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 添加filter http.addFilterBefore(new JwtAuthFilter(authenticationManager(), tokenManager), UsernamePasswordAuthenticationFilter.class); //登陆 http.formLogin().successHandler(loginSuccessHandler()).failureHandler(loginFailureHandler()) .permitAll(); // 无拦截 http.authorizeRequests().anyRequest().permitAll(); //退出 http.logout().logoutSuccessUrl("/login") .permitAll(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customUserService).passwordEncoder(passwordEncoder()); } /** * 密码加密 * * @return */ @Bean public BCryptPasswordEncoder passwordEncoder() { //密码加密 return new BCryptPasswordEncoder(4); } /** * 登录成功 * * @return */ @Bean AuthenticationSuccessHandler loginSuccessHandler() { return new AuthenticationSuccessHandler() { @Autowired private UserService userService; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth) throws IOException, ServletException { String account = request.getParameter("username"); String token = tokenManager.createTokenBusiness(account); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json,charset=utf-8"); response.getWriter().print("登录成功!token:" + token); } }; } /** * 登录失败 * * @return */ @Bean AuthenticationFailureHandler loginFailureHandler() { return new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { // Result result = new Result(ExceptionEnum.LOGIN_ERROR.getErrorCode(), ExceptionEnum.LOGIN_ERROR.getErrorMsg()); // response.setHeader("Content-Type", "application/json"); response.getWriter().print("登录失败!"); } }; } /** * 静态资源配置 * * @param web * @throws Exception */ @Override public void configure(WebSecurity web) { web.ignoring().antMatchers("/swagger-ui.html") .antMatchers("/webjars/**") .antMatchers("/v2/**") .antMatchers("/swagger-resources/**"); } }token工具类
package com.example.security.token; import com.example.security.model.User; import com.example.security.security.UserService; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * Created by zhangnianzhong on 2019/8/27. */ @Component("tokenManager") public class TokenManager { private static Logger logger = LoggerFactory.getLogger(TokenManager.class); @Autowired private UserService accountService; public String createToken(String userId) { long expire_time = 1000 * 60 * 60 * 12; String token = Jwts.builder() .setSubject(userId) .setExpiration(new Date(System.currentTimeMillis() + expire_time)) .signWith(SignatureAlgorithm.HS512, "JwtSecret") .compact(); return token; } /** * B端用户权限 * * @param userName * @return */ public String createTokenBusiness(String userName) { long expireTime = 1000 * 60 * 60 * 12; //过期时间为12小时 User account = accountService.getUser(userName); List<String> list= new ArrayList<>(); //因为用户没有设置管理权限,故默认都是ROLE_00权限,在接口加注解包含00,即是管理员权限的接口 if (account.isDev()) { list.add("ROLE_01"); } if (account.isChannel()) { list.add("ROLE_02"); } if (!account.isChannel() && !account.isDev()) { list.add("ROLE_00"); } String token = Jwts.builder() .signWith(SignatureAlgorithm.HS512, "JwtSecret") .claim("roles", String.join(",", list)) .setSubject(userName) .setExpiration(new Date(System.currentTimeMillis() + expireTime)) .compact(); return token; } /** * 模拟所有用户权限 * * @param userName * @return */ public String createTokenAll(String userName) { long expireTime = 1000 * 60 * 60 * 12; List<String> list= new ArrayList<>(); list.add("ROLE_00"); list.add("ROLE_01"); list.add("ROLE_02"); list.add("ROLE_10"); list.add("ROLE_11"); String token = Jwts.builder() .signWith(SignatureAlgorithm.HS512,"JwtSecret") .claim("roles", String.join(",", list)) .setSubject(userName) .setExpiration(new Date(System.currentTimeMillis() + expireTime)) .compact(); return token; } public String getRoles(String token) { try { Claims claims = Jwts.parser() .setSigningKey("JwtSecret") .parseClaimsJws(token) .getBody(); String roles = claims.get("roles").toString(); return roles; } catch (Exception e) { logger.info(e.getMessage()); } return null; } }获取当前的环境
@Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext context = null; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } // 传入线程中 public static <T> T getBean(String beanName) { return (T) context.getBean(beanName); } // 国际化使用 public static String getMessage(String key) { return context.getMessage(key, null, Locale.getDefault()); } /// 获取当前环境 public static String getActiveProfile() { return context.getEnvironment().getActiveProfiles()[0]; } /// 获取当前环境 public static String getProp(String prop) { return context.getEnvironment().getProperty(prop); } }swagger配置文件
@Configuration @EnableSwagger2 //@Profile({"test","dev","prod"}) public class Swagger2 { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.example.security.provider.controller")) .paths(PathSelectors.any()) .build() .globalOperationParameters(setHeaderToken()); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("权限版本二-V1-APIs") .description("") .termsOfServiceUrl("") .version("1.0") .build(); } // 添加token验证 private List<Parameter> setHeaderToken() { ParameterBuilder tokenPar = new ParameterBuilder(); List<Parameter> pars = new ArrayList<>(); tokenPar.name("token").description("token").modelRef(new ModelRef("string")).parameterType("header").required(false).build(); pars.add(tokenPar.build()); return pars; } }controller接口
@RestController @Api(tags = {"用户管理"},description = "带接口鉴权的用户管理接口") public class UserController { @PreAuthorize("hasAnyRole('00')") @GetMapping("getUser") @ApiOperation(value = "获取用户",notes = "获取用户") public User getUser(){ return new User("3","ceshi","789"); } }配置文件
server.port= 10010之后我们可以使用postman进行测试,使用security自带的login接口,是psot请求
二、请求头带上token访问后端接口
之后我们使用返回的token访问获取用户的接口,打开swagger接口(http://localhost:10010/swagger-ui.html),
因为默认都是00的权限,登录我是写死的,返回的一定也是00的权限,为了测试,我们修改获取用户借口中的注解参数
再次访问,出现403错误,无权限访问,现在的token是非00生成的,所以只能权限为非00的访问,这个返回结果我没有定义全局的异常处理,大家可以对异常进行再一次的封装
实例的连接
链接: https://pan.baidu.com/s/10ibBRxHEWakoENNBsvRALA 提取码: cd7u 复制这段内容后打开百度网盘手机App,操作更方便哦