Swagger+Spring开发Rest接口

Swagger+Spring开发Rest接口


流程

  • 1.利用Web工具Restlet Studio写Swagger文档
  • 2.利用SwaggerEditor生成Spring服务端代码
  • 3.在代码中整合SpringDataJPA
  • 4.在代码中整合JsonWebToken
  • 5.在代码中整合SpringSecurity

写Swagger文档

使用图形化web工具restlet studio
比较重要的步骤为:
1.创建Data Type
对应的是数据库实体类,或者Rest请求的交互数据模型,请求体和返回体内容尽量不要在接口中设置多个String参数,而是事先创建Data Type
模型创建
2.设置全局安全策略
这里我们用自定义的JWT所以选Custom类型
全局安全策略
3.设置接口安全策略
需要设置为继承自全局。同时还要设置Header
接口安全策略
4.添加资源方法
根据Rest规范Post Delete Put Get四个方法对应增删改查。其中GetDelete的Url应该是/resource/{id}。而PutPost则是/resource传json参数

5.简化方法
SpringDataJPA中,存和改都是save方法,我们开发的时候可以做简化,将Put方法归并到Post。另外我们还会有获取资源列表的接口。所以一般如下
简化方法
6.获取文档
生成文档

生成代码

http://editor.swagger.io中生成Spring服务端代码,生成的就是SpringBoot格式的代码。
生成代码

SpringDataJPA

Entity:生成的代码主要有俩文件夹,一个是model一个是api,我们在model中将实体类和数据库做关联。添加如下注解,其中@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})一定不要忘了,这是在转换json的时候将其加到json的一个字段里了,我们不需要所以忽略掉这些属性。

@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@Table(name = "device")
public class Device   {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(nullable = false)
  @JsonProperty("devid")
  private Integer devid = null;
}

Repository:新建一个Repository包,为实体类创建JpaRepository<Entity,PrimaryKeyType>

public interface DeviceDao extends JpaRepository<Device, Integer> {}

JsonWebToken

创建 Utils包来放工具类,例如我们借助jjwt的包封装JWTHelper类,下面是依赖和代码:
pom.xml

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.7.0</version>
</dependency>

JwtHelper.java

@Component
public class JwtHelper {
    @Value("${jwt.secret}")
    private  String stringKey;
    @Value("${jwt.expiration}")
    private long ttlMinute;
    public  String createJWT( String subject){
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256 ;
        long nowMillis = System.currentTimeMillis();
        long ttlMillis = ttlMinute*1000*60;
        Date now = new Date( nowMillis);
        SecretKey key = generalKey();
        JwtBuilder builder = Jwts.builder()
                .setIssuedAt(now)
                .setSubject(subject)
                .signWith(signatureAlgorithm, key);
        if (ttlMillis >= 0){
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date( expMillis);
            builder.setExpiration( exp);
        }
        return builder.compact();
    }
    public String parseJWT(String jwt){
        SecretKey key = generalKey();
        try{
            Claims claims = Jwts.parser()
                    .setSigningKey( key)
                    .parseClaimsJws(jwt).getBody();

            return claims.getSubject();
        }catch (Exception e){
            return null;
        }
    }
    private  SecretKey generalKey(){
        byte[] encodedKey = Base64.decodeBase64(stringKey);
        SecretKey key = 
            new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
}

注:jjwt的功能较多,这里只利用了subjectexpiresubject文本可以加密并且有存活时间,解密可以获取文本原文,如果超时了或者无法解密就返回null。两个public方法createJWT parseJWT就是加密和解密方法。
/login中产生JsonWebToken:

@Controller
public class LoginApiController implements LoginApi {
    @Autowired
    UserDao userDao;
    @Autowired
    JwtHelper jwtHelper;
    private final Log logger = LogFactory.getLog(this.getClass());

    public ResponseEntity<Token> loginPost(@ApiParam(value = "" ,required=true )  @Valid @RequestBody Login login) {
        Token token = new Token();
        User user=  userDao.getUserByUsername(login.getUsername());
        if(user.getPassword().equals(login.getPassword())){
            ObjectMapper mapper = new ObjectMapper();
            try {
                logger.info("write json to token:"+
                    mapper.writeValueAsString(user));
                user.setPassword("");//密码在token中没有用,且可能位数太长影响加解密速度
                token.setAccessToken(
                    jwtHelper.createJWT(mapper.writeValueAsString(user))
                    );
                return new ResponseEntity<Token>(token,HttpStatus.OK);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return new ResponseEntity<Token>(HttpStatus.UNAUTHORIZED);

    }
}

SpringSecurity

除了Authentication我们还需要Authorization,需要一个权限框架。
过滤器和安全设置
jwt过滤器:

@Component
public class JwtFilter extends OncePerRequestFilter {
    private final Log logger = LogFactory.getLog(this.getClass());
    @Autowired
    private JwtHelper JwtHelper;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        String authstr = request.getHeader("Authorization");
        if(authstr==null||!authstr.startsWith("Bearer ")){
            chain.doFilter(request, response);
            return;
        }
        String auth = authstr.substring(7);
        logger.info("auth:"+auth);
        String json = JwtHelper.parseJWT(auth);
        if (json!= null) {
            ObjectMapper mapper = new ObjectMapper();
            UserDetails userDetails=mapper.readValue(json, User.class);
            logger.info("get json from token:"+json);
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        chain.doFilter(request, response);
    }
}

这样在application.properties中设置密钥和过时时间(分钟)

jwt.secret=mesh
jwt.expiration=60

另外上面用到了UserDetails接口类User需要事先创建,且需要添加注解:

@JsonIgnoreProperties({"hibernateLazyInitializer", "handler","accountNonLocked","credentialsNonExpired","enabled","authorities","accountNonExpired",})

为了之后Jwt加解密的Subject直接用User的json字符串,加密时直接转成json后加密,解密直接将json字符转成对象。方便~
跨域过滤器[替换swagger生成的]

@Component
public class ApiOriginFilter extends OncePerRequestFilter {
    @Override
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                 FilterChain chain) throws IOException, ServletException {
        HttpServletResponse res = (HttpServletResponse) response;
        res.addHeader("Access-Control-Allow-Origin", "*");
        res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS");
        res.addHeader("Access-Control-Allow-Headers", "Content-Type");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
    }
}

web安全设置

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConf extends WebSecurityConfigurerAdapter{
    @Autowired
    JwtFilter jwtFilter;
    @Autowired
    ApiOriginFilter originFilter;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //rest接口中csrf session不需要
        http
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                //以下url允许任何人访问
                .antMatchers("/login","/api-docs","/h2-console/**","/swagger-resources/**","/**/*.html").permitAll()
                //其他的请求get方法user和admin都可以访问,但post和delete需admin
                .antMatchers(HttpMethod.GET).hasAnyRole("USER","ADMIN")
                .antMatchers(HttpMethod.DELETE).hasRole("ADMIN")
                .antMatchers(HttpMethod.POST).hasRole("ADMIN")
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .anyRequest().authenticated()
                .and()
                //关了同源限制,为了能在无session时使用h2-console
                .headers().frameOptions().disable();
        //在SpringSecurity权限过滤器前加我们的jwtFilter        
        http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
        //在更前面添加跨域过滤器
        http.addFilterBefore(originFilter, JwtFilter.class);
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值